.NET Forum / Languages / C# / February 2008
Winforms, Shown Event and SetEnvironmentVariable
|
|
Thread rating:  |
Dilip - 14 Feb 2008 21:50 GMT I am running into a weird problem in my ultra-simple Winforms application written in C#.
In the Form.Shown event I set a couple of environment variables using the standard SetEnvironmentVariable API like so:
Environment.SetEnvironmentVariable("someKey", somevalue, EnvironmentVariableTarget.User);
I have 2 such calls. Amazingly it takes nearly 10 seconds for these two calls to execute! If I comment out these 2 calls and run, the application is just spiffy and the dialog comes up in no time. Because it takes 10 seconds for these calls to execute, the dialog kinda hangs before becoming responsive for that duration of time (obviously because the UI thread is held up).
Am I doing something wrong?
Dilip - 14 Feb 2008 22:06 GMT > I am running into a weird problem in my ultra-simple Winforms > application written in C#. [quoted text clipped - 13 lines] > > Am I doing something wrong? Apologies.. false alarm! It was a different problem.
Peter Duniho - 14 Feb 2008 22:21 GMT > Apologies.. false alarm! > It was a different problem. What was it? As I mentioned, I was able to easily reproduce what you described. Are you saying that you're not actually having that problem?
Pete
Dilip - 15 Feb 2008 13:34 GMT On Feb 14, 4:21 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > Apologies.. false alarm! > > It was a different problem. [quoted text clipped - 3 lines] > > Pete Peter I am sorry once again. Its now a false alarm on top of a false alarm :-) My original post was accurate. Those two calls to the SetEnvironmentVariable does take nearly 10 seconds to execute (I timed it with System.Diagnostics.Stopwatch).
This is weird because I earlier had these calls in a console app and I never saw this delay. Putting them in a Winforms app however introduces it. I can't understand why.
I am going to run CLR profiler and see what is bottlenecking it. I haven't read your other post yet. Will get to it after I post this.
Peter Duniho - 14 Feb 2008 22:20 GMT > [...] > In the Form.Shown event I set a couple of environment variables using [quoted text clipped - 7 lines] > > Am I doing something wrong? You mean, other than setting an environment variable? :)
I'm surprised it takes 10 seconds for the calls to execute. I'd agree that seems unreasonably long. However, I can easily reproduce the exact behavior (almost down to the time...11 seconds on my computer, though I did see it take "only" 8 seconds once).
Also surprising is that I wasn't able to find any mention of this issue using Google. I tried a few different searches and a quick look at the results turned up no other mentions of the delay. It makes me wonder if this is a new problem, possibly related to a recent Windows Update.
But why are you setting environment variables anyway? That's kind of an archaic thing to do. Do you have a requirement for compatibility with some older software that uses the variables?
As far as dealing with the issue: I think the best solution is "don't do that". That is, don't use environment variables if you don't absolutely have to. I think the alternative (short of figuring out why it takes so long and possibly finding an alternative) would be to move those operations out of the GUI thread. You can use the BackgroundWorker class to execute them using the thread pool and then notify your Form class when the operation is done (so that it can update itself as appropriate).
One thing I didn't try was using unmanaged code to set the environment variables. That's something you might try, in order to at least understand if the issue is something unique to .NET, or is a general problem with setting environment variables. And if it is significantly faster using unmanaged code, you might try using p/invoke with the unmanaged API to set the variables rather than going through .NET.
Pete
Dilip - 15 Feb 2008 14:03 GMT On Feb 14, 4:20 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> > [...] > > In the Form.Shown event I set a couple of environment variables using [quoted text clipped - 9 lines] > > You mean, other than setting an environment variable? :)
> I'm surprised it takes 10 seconds for the calls to execute. I'd agree > that seems unreasonably long. However, I can easily reproduce the exact [quoted text clipped - 9 lines] > archaic thing to do. Do you have a requirement for compatibility with > some older software that uses the variables? Nope.. no such requirement. Its just that my application is kinda funny in the sense it runs multiple times with different command line parameters. I had to maintain some state between invocations. I could have used the registry obviously but went the Environment variable route for no particular reason. As I said I initially built this as a console application in C# for easy testing. Once I was sure everything is working fine I built an UI around it -- which brings me to your next point...
> As far as dealing with the issue: I think the best solution is "don't do > that". That is, don't use environment variables if you don't absolutely [quoted text clipped - 3 lines] > to execute them using the thread pool and then notify your Form class when > the operation is done (so that it can update itself as appropriate). ... I do exactly this :-) The functionality I tested when my app was still a console app has been nicely wrapped up in a BackgroundWorker class. Its just that setting the environment variable didn't strictly belong there. So I set it before invoking BackgroundWorker.RunAsync(...). Little did I realize that these SetEnvironmentVariable calls will take up so much time. For the moment I put them in Form.Load event, so the the form doesn't even show up until after the variables are set.
But I am still curious as to why this problem is not cropping up with a plain vanilla console application!
This also brings up a related question. The UI and this functionality are two separate beasts. The goal is I must be able to rip out this functionality and use it elsewhere (maybe as a standalone console app) without worrying about tightly-binding it to the UI logic. However the problem is if I use the BackgroundWorker class and if I want to report the progress percentage to the UI from the functionality, I have no choice but to pass a reference to the BackgroundWorker class to the said functionality, right? Do I now strongly-couple UI with business logic?
Peter Duniho - 15 Feb 2008 23:35 GMT > [...] >> But why are you setting environment variables anyway? That's kind of an [quoted text clipped - 6 lines] > could have used the registry obviously but went the Environment > variable route for no particular reason. Well, the environment variables wind up in the registry anyway. IMHO, if that's acceptable to you, you should probably just use the registry directly.
In a .NET application, the other alternative would be a configuration file. In .NET 2.0 and later, the Settings class provides a very nice, easy way to persist user-specific data and IMHO would be a much better solution than using environment variables.
> [...] > But I am still curious as to why this problem is not cropping up with > a plain vanilla console application! I'm curious too. There's probably a reasonably obvious explanation for someone who knows more about the exact implementation. I did notice when I was looking into this yesterday that the unmanaged Win32 API doesn't actually provide for this functionality. The docs say to implement it yourself by saving the data to the registry directly, and then broadcasting a WM_SETTINGCHANGED message.
Assuming .NET took that advice, it makes me wonder if there's some sort of interaction with the broadcast of that message, but I don't know what it would be. Possibly the broadcast has to wait for a timeout because your application is stuck trying to change the registry and can't receive it? That would explain why the console application works fine, as it may skip the window message broadcasting, or at least not having a window itself would not wind up being a recursive bottleneck in the broadcast. It would also be consistent with the delay being practically identical on my computer as on yours (i.e. it's not dependent on hardware configuration, but rather some built-in timing issue).
Of course, under that theory it should be the case that once the setting of the registry settings are moved to another thread, it should not take so long. So, I tried that. And in fact, doing so makes it work a _lot_ faster. It's still slower than I'd think, but on my computer it completes in between eight to nine tenths of a second when executing the environment variables on a separate thread. Given all the overhead for dealing with the broadcast of the window message, thread context switches, etc. maybe that's not an unreasonable amount of time. It's definitely a lot better than 10 seconds. :)
So: have I proved what's going on? Nope...I still don't really know what's going on. But there's some decent evidence in favor of my theory.
:)
> This also brings up a related question. The UI and this functionality > are two separate beasts. The goal is I must be able to rip out this [quoted text clipped - 4 lines] > have no choice but to pass a reference to the BackgroundWorker class > to the said functionality, right? I'm not sure exactly what you mean, but generally the answer is "no". BackgroundWorker does not depend on a GUI at all. You do not need to subscribe to the ProgressChanged event, and even if you do, the subscriber does not need to be a GUI component. Without the synchronization context that the GUI provides, the event will be raised in the same thread doing the work, rather than marshaled over to the GUI thread. But otherwise it works fine.
> Do I now strongly-couple UI with business logic? That shouldn't be necessary. If you still think it is, maybe you can explain in greater detail why you think it is. That would help provide the context so that I or someone else can better explain why it's not. :)
Pete
Willy Denoyette [MVP] - 16 Feb 2008 17:24 GMT > On Feb 14, 4:20 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> > wrote: [quoted text clipped - 65 lines] > to the said functionality, right? Do I now strongly-couple UI with > business logic? I can't repro this using a minimal Forms applications that sets two environment variables in the Shown handler. The SetEnvironmentVariable calls take less than 2 millisecond. to complete. Are you running in a local account, or are you running in a domain account? What version of the framework and OS are you running?
Willy.
Peter Duniho - 16 Feb 2008 18:47 GMT > I can't repro this using a minimal Forms applications that sets two > environment variables in the Shown handler. [quoted text clipped - 3 lines] > account? > What version of the framework and OS are you running? I was able to reproduce the behavior easily. So, from my configuration:
Windows XP SP2, IE7 (it shouldn't matter, but as we know it sometimes does), all critical updates installed (including this month's release). .NET 2.0 installed (SP1, if I recall correctly). User is a restricted/limited user, local account, and the target for the call to SetEnvironmentVariable is EnvironmentVariableTarget.User.
As I noted in a different message, running the code in the GUI thread results in a 10 second delay (I didn't measure the two calls independently, so I don't know if it's a single 10 second delay, or two 5 second delays). Running the same exact code in a thread pool thread (via BackgroundWorker) results in a much smaller delay (700-900ms...still longer than I'd expected, but much more acceptable).
Are you sure you're using the EnvironmentVariableTarget.User as the target in your tests?
Pete
Willy Denoyette [MVP] - 16 Feb 2008 21:42 GMT >> I can't repro this using a minimal Forms applications that sets two >> environment variables in the Shown handler. [quoted text clipped - 21 lines] > Are you sure you're using the EnvironmentVariableTarget.User as the target > in your tests? Yep, these two: Environment.SetEnvironmentVariable("someKey", "1", EnvironmentVariableTarget.User); Environment.SetEnvironmentVariable("someOtherKey", "2", EnvironmentVariableTarget.User);
in Shown handler.
Running V2 SP1 of the FW on VISTA. Timing done using "QueryThreadCycleTime" API (Vista and up), with Processor clock at fixed rate. Total time for the two consecutive calls: ~3.6 msec. on 2.2Ghz Dual core AMD64 ~3 msec. on 3GHz Intel Core Duo.
Willy.
Peter Duniho - 17 Feb 2008 00:49 GMT > Yep, these two: > Environment.SetEnvironmentVariable("someKey", "1", [quoted text clipped - 7 lines] > Timing done using "QueryThreadCycleTime" API (Vista and up), with > Processor clock at fixed rate. Why are you doing that? The question here is elapsed time, not the CPU time consumed by the thread for the calls.
If you just put a Stopwatch in the Shown event handler, start it before the two calls, and check the Elapsed time after them, what results do you get?
Not that it's relevant to this question IMHO, but what's the .NET API for the QueryThreadCycleTime call (if any)? I can only find the unmanaged version. Is there a managed way to get at that? Maybe in WMI?
Pete
Willy Denoyette [MVP] - 17 Feb 2008 17:45 GMT >> Yep, these two: >> Environment.SetEnvironmentVariable("someKey", "1", [quoted text clipped - 10 lines] > Why are you doing that? The question here is elapsed time, not the CPU > time consumed by the thread for the calls. One reason is higher precision, initially I used Stopwatch and soon found out that the elapsed time was way below 1 second (see later), also, I don't like to use Stopwatch for anything that takes less than a second. The other reason is that I wanted to measure the time taken by the *current* UI thread to perform these two API calls, not the time taken by the other threads in the system. The reason is that SetEnvironmentVariable calls Win32's "SendMessageTimeout" to broadcast a message to all top level windows (except the owning window) and does not return before the message has been processed by all *other* top level windows or after a 1000 msec. time-out period (per window). When the system broadcasts a message to a top-level window who's thread doesn't pump it's message queue, it will wait a max of 5 seconds before returning, which looks like what's happening in your (and the OP's) case, though I don't understand why it's working from a console app.
> If you just put a Stopwatch in the Shown event handler, start it before > the two calls, and check the Elapsed time after them, what results do you > get? ~90-100 msecs. using ... double elapsed = ((double)sw.ElapsedTicks/swFrequency); with ~10 top-level windows. This is considerably longer merely because of the extra thread switches
> Not that it's relevant to this question IMHO, but what's the .NET API for > the QueryThreadCycleTime call (if any)? I can only find the unmanaged > version. Is there a managed way to get at that? Maybe in WMI? QueryThreadCycleTime, QueryProcessCycleTime, QueryIdleProcessorCycleTime and a couple of others are new Win32 API's in Vista and up. .NET lags behind here, so the only way to use these high precision low latency counters is by PInvoke. WMI does/can't expose this, but the VS profiler can be configured to use these counters, also ETW uses the high precision counters on Vista and WS2008.
Willy.
Willy Denoyette [MVP] - 17 Feb 2008 18:03 GMT >>> Yep, these two: >>> Environment.SetEnvironmentVariable("someKey", "1", [quoted text clipped - 48 lines] > > Willy. Please note that I don't have handlers for WM_SETTINGCHANGE in the other running windows applications, other than the default handler.
Willy.
Dilip - 17 Feb 2008 04:26 GMT On Feb 16, 12:47 pm, "Peter Duniho" <NpOeStPe...@nnowslpianmk.com> wrote:
> As I noted in a different message, running the code in the GUI thread > results in a 10 second delay (I didn't measure the two calls > independently, so I don't know if it's a single 10 second delay, or two 5 > second delays). I can confirm that its two 5-second delay.
The funny thing is I can't glean anything useful out of CLR profiler. I thought I'd chase the calls and see exactly what is taking so long but didn't have much success.
BTW, Willy seems to have tested this in Vista. I haven't run it there yet -- my problem was exhibited in Windows XP. I don't have .NET 2.0 SP1 installed.
THe Settings class seems like a nice idea (yet another thing I can claim to know :-)). I am going down that road but I'd be curious to know what the problem is though. As of now your explanation in another post seems to make sense to me.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|