WPF窗口句柄仅用于消息循环

6
我正在编写一个 WPF 应用程序,它将在系统托盘中放置一个图标。为了练习,我想做到不依赖于 System.Windows.Forms 并使用其 NotifyIcon 或 NativeWindow 类来完成此操作。
这相当容易 - 从 C# 调用 Shell_NotifyIcon 不难 - 我已经成功完成了我的任务。
作为此努力的一部分,我不得不创建一个窗口句柄,只是为了接收来自系统托盘的消息。我按照以下方式创建本地窗口:
// Create a 'Native' window
_hwndSource = new HwndSource(0, 0, 0, 0, 0, 0, 0, null, parentHandle);
_hwndSource.AddHook(WndProc);

消息循环在AddHook()中被挂钩,消息会在一个类似于下面这样的函数中被处理:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    // Handle windows messages in this...
}

最后,当需要销毁该对象时,我通过发送WM_CLOSE消息并处理HwndSource来关闭窗口。

if (null != _hwndSource)
{
    UnsafeNativeMethods.PostMessage(_hwndSource.Handle, WindowMessage.WM_CLOSE, 0, 0);
    _hwndSource.Dispose();
    _hwndSource = null;
}

我的问题是: HwndSource构造函数的前三个参数分别是本地Win32窗口的类样式、样式和扩展样式。对于一个只用作窗口消息目标的不可见窗口,它们应该是什么?

我使用了默认值零、零和……嗯……零,但我使用Spy++来检查Windows.Forms.NotifyIcon的操作,它看起来创建了以下的NativeWindow

Class Style:     <zero>
Styles:          WS_CAPTION, WS_CLIPSIBLINGS,
                 WS_OVERLAPPED
Extended Styles: WS_EX_LEFT, WS_EX_LTRREADING,
                 WS_EX_RIGHTSCROLLBAR, WS_EX_WINDOWEDGE

这些对于一个不可见的窗口来说有用吗?我认为不需要。
1个回答

7

Windows风格的标志始于1986年,当时发布了Windows v1.0。在过去的29年和10个主要版本中,已经有很多应用兼容性的黑客,当应用程序指定奇怪的标志时,Windows会默默地覆盖样式标志。这并不是什么太奇怪的事情,但请注意,WS_OVERLAPPED样式标志的值为0。这要求一个普通窗口,您自动获得适用于此类窗口的适当样式标志。

您的HwndSource窗口具有完全相同的样式标志,也许您在Spy ++中没有找到正确的标志。所以你没有问题。而且,当窗口永远不可见时,它们并不重要。

请注意您代码中的错误,您发送的WM_CLOSE消息实际上从未被处理,因为您在调用PostMessage()之后立即销毁了窗口。只需将其删除,请求窗口很好,它不会反对。但是,您必须使用NIM_DELETE调用Shell_NotifyIcon()来删除托盘图标。如果不这样做,就会留下一个“幽灵”图标,只有当您将鼠标移到上面时才会消失。

请注意,NotifyIcon并不像您认为的那样简单,它有一个非常明显的错误解决方法,您很可能会忽略它。当上下文菜单拒绝关闭时,您会注意到。


1
在本地Windows API编程中,您可以在此场景中使用仅消息窗口。这也可以与HwndSource一起使用吗?源代码对我来说并没有太大的启示。 - IInspectable
是的,HwndSource没有问题。请使用new IntPtr(-3)代替parentHandle - Hans Passant
谢谢大家。这些正是我需要的事实。我已经忘记了HWND_MESSAGE - 这可能更正确地表示父hwnd。事实上,我知道“非显而易见的错误解决方法”,但还是谢谢 - 我相信它对其他读者有帮助,以及关于NIM_DELETE的信息已经成为我的代码的一部分,但我没有考虑它与问题相关。此外,如果你说我不需要发布WM_CLOSE,因为我正在处理HwndSource,那么我会把它(post-message)拿出来。(WinForms的NativeWindow发布WM_CLOSE,供参考。) - Xharlie
1
似乎你不能在任务栏图标后面的消息循环中实际使用仅限消息的窗口。首先,该消息循环的主要职责之一是捕获资源管理器的“TaskbarCreated”消息,该消息仅发送给顶级窗口。其次,前面提到的非明显的错误解决方法要求在显示上下文菜单之前将窗口变为前景窗口。HWND_MESSAGE的子级永远不会成为顶级窗口,至少根据我生疏的Win32知识是这样的。 - Xharlie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接