views:

53

answers:

3

I'm trying to invoke a dialog on the UI dispatcher :

class DialogService : IDialogService
{
    private readonly Dispatcher _dispatcher = Application.Current.Dispatcher;

    public bool? Show(IDialogViewModel viewModel)
    {
        if (_dispatcher.CheckAccess())
        {
            var dialogWindow = new DialogWindow();
            return dialogWindow.Show(viewModel);
        }
        else
        {
            Func<IDialogViewModel, bool?> func = Show;
            return (bool?)_dispatcher.Invoke(func, viewModel);
        }
    }
}

However, the call to Invoke blocks forever, and Show is never called on the UI thread...

Using BeginInvoke is not an option : I need the result immediately, because I'm handling an event from a remote object (using .NET remoting)

Any idea ?


UPDATE

Here is a more complete description of the problem :

I have an client application that communicates with a Windows service using .NET Remoting. At some point, the client makes a call to the service to perform an operation (this call is triggered by a user action, a click on a button in that case). The service might need credentials to perform the operation: in that case, it raises a CredentialsNeeded event, handled by the client. The client then shows a dialog to prompt the user for credentials, and sets the appropriate properties in the event's arguments. When the event handler returns, the service uses the credentials to complete the operation, and returns control to the client.

So, when I receive the event, the UI thread is waiting for an operation to complete on the service side... I assume it's the reason why the Invoke call is not processed, but how can I work around it ? Can I create another UI thread to show the dialog ? In WinForms, I know I could start another message pump with Application.Run, but I don't know how to do the same in WPF...

+1  A: 

Do you own a lock during this method call which another method on the UI thread is trying to acquire? That would certainly explain it.

Does this happen every time? That would obviously make it easier to diagnose.

Unusually for me, I'd suggest going to the debugger: just hit break and see what the threads are doing.

Finally, I know you need the result... but what happens if you do call BeginInvoke instead (and return a dummy value)? Does that invoke the method in the dispatcher or not? Obviously this wouldn't be a long term fix, but it would give more diagnostic information.

Jon Skeet
Thanks for your answer. I'm not doing any explicit locking in that part of the code, so I don't think the problem is there. And yes, it does happen every time... Actually I think I'm starting to understand what is going wrong (see my updated question), I just don't know how to fix it...
Thomas Levesque
+1  A: 

Is the UI thread making a blocking call to something else (perhaps your background thread) at the time you are trying to use Invoke? If so then you have a classic deadlock on your hands. Two threads each waiting for the other to return.

In Windows Forms they do a lot of behind the scenes "message pumping" often when you least expect it in an effort to avoid deadlocks but many times it creates even more problems and hard to find bugs due to unexpected re-entrancy.

If you don't think your UI thread is in the process of making a blocking call, you should run the application in the debugger and break into the debugger when the deadlock occurs. Then look in the Threads window for the main thread. Double click the main thread then look at the Call Stack window to see where the main thread is sitting.

You can also try explicitly specifying a DispatcherPriority of Send though I don't think that will matter if there's a true deadlock.

Josh Einstein
Thanks for your answer. Indeed, my main thread is making a blocking call to a remote object, which is itself making a blocking call back to the client... The remote object is raising an event to prompt the user for credentials. That's where I'm trying to show a dialog. See my updated question for details.
Thomas Levesque
A: 

I eventually found a solution to my problem : I just need to show the dialog on a new thread, with its own dispatcher. Here's the modified code :

class DialogService : IDialogService
{
    private readonly Dispatcher _dispatcher = Application.Current.Dispatcher;

    public bool? Show(IDialogViewModel viewModel)
    {
        if (_dispatcher.CheckAccess())
        {
            DoShow(viewModel);
        }
        else
        {
            bool? r = null;
            Thread thread = new Thread(() => r = DoShow(viewModel));
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            thread.Join();
            return r;
        }
    }

    private static bool? DoShow(IDialogViewModel viewModel)
    {
        var dialogWindow = new DialogWindow();
        return dialogWindow.Show(viewModel);
    }
}
Thomas Levesque