【问题标题】:How to intercept WndProc messages from a HwndSource without any content如何从没有任何内容的 HwndSource 拦截 WndProc 消息
【发布时间】:2012-08-30 18:34:21
【问题描述】:

我正在尝试与使用 HWND 将消息传递回调用方的本机库进行通信,如下所示:

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

即使省略了对本机库的调用,我也只会收到以下消息(按顺序):

  1. 0x0001:WM_CREATE
  2. 0x0018:WM_SHOWWINDOW
  3. 0x0046:WM_WINDOWPOSCHANGING
  4. 0x0046:WM_WINDOWPOSCHANGING
  5. 0x001C:WM_ACTIVATEAPP
  6. 0x0086:WM_NCACTIVATE
  7. 0x007F:WM_GETICON
  8. 0x007F:WM_GETICON
  9. 0x007F:WM_GETICON
  10. 0x0006:WM_ACTIVATE
  11. 0x0281:WM_IME_SETCONTEXT
  12. 0x0282:WM_IME_NOTIFY
  13. 0x0007:WM_SETFOCUS
  14. 0x0085:WM_NCPAINT
  15. 0x0014:WM_ERASEBKGND
  16. 0x0047:WM_WINDOWPOSCHANGED
  17. 0x0083:WM_NCCALCSIZE
  18. 0x0085:WM_NCPAINT
  19. 0x0014:WM_ERASEBKGND
  20. 0x0005:WM_SIZE
  21. 0x0003:WM_MOVE
  22. 0x000D:WM_GETTEXT

之后,我可以拖动与HwndSource 对应的窗口,但无法调整大小或关闭它。此外,操作系统声称此窗口没有响应。

为什么这个窗口会停止响应,我该如何继续拦截消息?

【问题讨论】:

  • 丢失while (true) { } 代码。

标签: c# .net wpf interop hwnd


【解决方案1】:

我认为当您尝试从 Windows 队列中检索下一条消息时,它会冻结您正在等待来自 WndProc 响应的原始线程。

尝试通过Application.AddMessageFilter 拦截消息,就像在this other question 中所做的那样

【讨论】:

  • 我同意你应该在完成后移除钩子,但这并不能解释为什么示例代码停止拦截消息在钩子应该被释放之前.
【解决方案2】:

@HansPassant 和 @HassanBoutougha 都指出问题出在以下代码段:

while (true) { }

虽然这看起来像是在无辜地保持应用程序处于活动状态,但实际上它正在阻止调度程序处理消息。我看到的消息是因为它们是由HwndSource 构造函数直接从执行Example() 的线程中调用的。构建后,应用程序进入循环并绕圈运行。不幸的是,这是处理消息的线程!

这里基本上正确的解决方案是告诉调度程序将当前调用堆栈放在次要位置并处理事件,直到我们告诉它停止。

DipatcherFrame frame = new DispatcherFrame();
Dispatcher.PushFrame(frame);

本质上是对Dispatcher.PushFrame 的调用告诉调度程序暂停执行并继续收听消息。在未来的某个时候,如果您决定要从离开的地方继续执行,只需执行以下操作:

frame.Continue = false;

就是这样!对Dispatcher.PushFrame 的调用现在将返回并继续执行

Kent Boggart 有一个很好的例子:http://kentb.blogspot.ca/2008/04/dispatcher-frames.html 不幸的是,Kent 的博客不再可用,但可以在 here 找到来自 Google 缓存的页面快照。

【讨论】:

  • @Nate-Wilkins 我添加了指向 Google 页面缓存的链接。
  • @ChrisKerekes 指向 Google 缓存页面的链接也失效了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-09-08
  • 2017-01-07
  • 2015-09-27
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 2012-03-11
相关资源
最近更新 更多