Dispatcher.Invoke blocks forever
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 r开发者_Go百科emote 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...
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.
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);
}
}
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.
精彩评论