views:

991

answers:

3

DISCLAIMER: The following code is not something I would ever use in a real application. I stumbled on this problem and would like to know what's happening under the hoods.

Let's assume that for some crazy reason we had the following code...

using (System.Net.WebClient webClient = new System.Net.WebClient())
{
    bool done = false;
    webClient.UploadFileCompleted += (s, e) => done = true;

    string uploadUri = "ftp://www.notarealurl.com/";
    string file = @"temp.txt";

    webClient.UploadFileAsync(
        new Uri("ftp://www.notarealurl.com/"), "temp.txt");

    while (!done)
    {
        System.Threading.Thread.Sleep(100);
    }
}

This code works as expected in a simple console app. However, move it to a WPF app and it never exits the "while" loop. This through me for a loop for a while until it occured to me that this might be an issue with the events being dropped in the message pump and therefor not being processed. To test this, I modified the "while" loop like this...

while (!done)
{
    System.Threading.Thread.Sleep(100);

    // Processes all messages currently in the message queue
    Dispatcher.Invoke(
        DispatcherPriority.Background, new ThreadStart(delegate { }));
}

Sure enough, that fixed it. So,on to the question...

What is going on here? When I run this code in a console app the event handler is executed on a worker thread. Why/how does it get put on the main thread in WPF?

+1  A: 

I think in WPF, it tries to put all callbacks on the thread that they came from. This is called the "affinity" of the thread.

FryGuy
+1  A: 

Iמ WPF (and also in WinForms, Win32 and any other UI library I've ever worked with) you can only access a UI object from the same thread that created it.

Also, in any GUI Windows program you have a message loop and message dispatching, in a console application you don't.

So it looks like either

  1. WebClient detects that it is running in a GUI application and tries to be helpful by raising events in the same thread that activated it.

or

  1. WebClient uses message dispatching internally and falls back to worker threads in console applications only.
Nir
+1  A: 

Ok, I did a little decompiling with Reflector and I've worked out what's going on under the hoods. The web client makes use of a SynchronizationContext class to handle putting callbacks on the original thread. I was not familiar with the SynchronizationContext class, but there's a really nice article about it here...

http://www.codeproject.com/KB/cpp/SyncContextTutorial.aspx

herbrandson