I'm trying to communicate with a native library that uses an HWND
to pass messages back to the caller as follows:
private void Example()
{
using (
var hwnd = new HwndSource(
new HwndSourceParameters("I sense a disturbance in the force...") {HwndSourceHook = WndProc}
)
)
{
//send hwnd.handle to native library
while (true) { }
}
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool ishandled)
{
ishandled = false;
Console.WriteLine("Intercepted message: 0x{0:X}", msg);
return IntPtr.Zero;
}
Even when the call the the native library is omitted, I only ever receive the following messages (in order):
WM_CREATE
WM_SHOWWINDOW
WM_WINDOWPOSCHANGING
WM_WINDOWPOSCHANGING
WM_ACTIVATEAPP
WM_NCACTIVATE
WM_GETICON
WM_GETICON
WM_GETICON
WM_ACTIVATE
WM_IME_SETCONTEXT
WM_IME_NOTIFY
WM_SETFOCUS
WM_NCPAINT
WM_ERASEBKGND
WM_WINDOWPOSCHANGED
WM_NCCALCSIZE
WM_NCPAINT
WM_ERASEBKGND
WM_SIZE
WM_MOVE
WM_GETTEXT
After this I can drag the window corresponding to the HwndSource
around but am unable to resize or close it. Furthermore the operating system claims that this window is not responding.
Why does this window stop responding and how can I continue to intercept messages?
As both @HansPassant and @HassanBoutougha pointed out the issue is with the following segment of code:
while (true) { }
While this may look like it's innocently keeping the application alive what it's really doing is preventing the dispatcher from processing messages. The messages that I was seeing was because they were being called directly from within the thread executing Example()
by the HwndSource
constructor. After construction the application enters the loop and runs around in circles. Unfortunately this is the thread that's suppose to be processing the messages!
Essentially the correct solution here was to tell the dispatcher to put the current call stack on the back burner and process events until we tell it to stop.
DipatcherFrame frame = new DispatcherFrame();
Dispatcher.PushFrame(frame);
Essentially the call to Dispatcher.PushFrame
this tells the dispatcher to suspend execution and continue listening to messages. At some point in the future if you decide that you want to resume execution where you left of just do the following:
frame.Continue = false;
And that's it! The call to Dispatcher.PushFrame
will now return and resume execution
Kent Boggart has a great example here: http://kentb.blogspot.ca/2008/04/dispatcher-frames.html
Unfortunately Kent's blog is no longer available however a snapshot of the page from Google's cache may be found here.