【问题标题】:WPF Window Handle for Message Loop Only仅用于消息循环的 WPF 窗口句柄
【发布时间】:2015-04-06 12:35:20
【问题描述】:

我正在编写一个 WPF 应用程序,它将在系统托盘中放置一个图标,作为练习,我想在不依赖 System.Windows.Forms 并使用其 NotifyIconNativeWindow 类的情况下执行此操作。

这相当简单 - 从 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

这些对于不可见的窗口是否重要? (我认为不是。)

【问题讨论】:

    标签: c# wpf windows winapi


    【解决方案1】:

    Windows 样式标志可以追溯到 1986 年,早在 Windows v1.0 发布时。在过去的 29 年和 10 个主要版本中,有 很多 appcompat hack,当应用程序指定不可靠的样式标志时,Windows 会默默地覆盖样式标志。然而,这并没有什么可怕的,请注意 WS_OVERLAPPED 样式标志的值为 0。它要求一个普通的窗口,您会自动为这样的窗口获取适当的样式标志。

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

    请注意您的代码中的一个错误,您发布的 WM_CLOSE 消息从未真正处理过,因为您在调用 PostMessage() 后立即销毁了窗口。把它删了,好好问窗口也没用,它不会反对的。但是,您必须使用 NIM_DELETE 调用 Shell_NotifyIcon() 才能删除托盘图标。否则会留下一个“幽灵”图标,仅当您将鼠标移到该图标上时才会消失。

    请注意,NotifyIcon 并不像您想象的那么简单,它有一个您可能会忽略的non-obvious bug workaround。当上下文菜单拒绝关闭时,您会注意到。

    【讨论】:

    • 在本机 Windows API 编程中,在这种情况下您将使用 message-only window。这也可以与HwndSource 一起使用吗? source code 对我的启发并不大。
    • 是的,HwndSource 不反对。使用 new IntPtr(-3) 而不是 parentHandle
    • 谢谢大家。这些正是我所要求的事实。我忘记了 HWND_MESSAGE - 这对于父 hwnd 可能更正确。事实上,我确实知道“非显而易见的错误解决方法”,但无论如何,谢谢 - 我确信它会对这个问题的其他读者有所帮助,关于 NIM_DELETE 的信息也已经是我的代码的一部分,但我认为它与问题无关。另外,如果您说我不需要发布 WM_CLOSE,因为我正在处理 HwndSource,我会删除它(发布消息)。 (WinForms 的 NativeWindow 发布 WM_CLOSE,以供记录。)
    • 似乎您实际上不能将仅消息窗口用于任务托盘图标后面的消息循环。首先,该消息循环的主要职责之一是捕获 Explorer 的 TaskbarCreated 消息,该消息仅发送到顶级窗口。其次,上述非显而易见的错误解决方法要求窗口在显示上下文菜单之前成为前景窗口。 HWND_MESSAGE 的孩子永远不是顶级的,也不能成为前台窗口,至少根据我生疏的 Win32 知识。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-06
    • 2012-05-26
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多