Winforms, Shown Event and SetEnvironmentVariable

  • Thread starter Thread starter Dilip
  • Start date Start date
D

Dilip

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?
 
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?

Apologies.. false alarm!
It was a different problem.
 
[...]
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! [...]

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
 
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
 
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

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.
 
[...]
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! [...]
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?

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
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).

.... 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?
 
[...]
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?

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.

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
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?

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
 
Dilip said:
[...]
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! [...]
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?

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
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).

... 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?


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.
 
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?

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
 
Peter Duniho said:
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?

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.
 
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.

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
 
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.
 
Peter Duniho said:
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 said:
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.

~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

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.



Please note that I don't have handlers for WM_SETTINGCHANGE in the other
running windows applications, other than the default handler.

Willy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top