如何在 .Net 6 中处理 Win32 WndProc 消息?

How to handle Win32 WndProc messages in .Net 6?

我正在使用 .Net 6 和 PInvoke nuget 包来访问 Win32 API 并且我正在通过以下方式创建 Win32 window:

  IntPtr windowHandle = User32.CreateWindowEx(User32.WindowStylesEx.WS_EX_TOOLWINDOW,
                               "static",
                               "Window Title",
                               User32.WindowStyles.WS_OVERLAPPEDWINDOW |
                               User32.WindowStyles.WS_VISIBLE,
                               0,
                               0,
                               800,
                               800,
                               IntPtr.Zero,
                               IntPtr.Zero,
                               IntPtr.Zero,
                               IntPtr.Zero);

当 window 在屏幕上可见时,我 the same situation as this guy window 呈现良好,但对用户没有响应。当我将鼠标悬停在 window 上时,鼠标指针变为加载圆圈。

我认为无响应是由于 window 事件和消息没有得到处理。我想以某种方式覆盖或连接到 Win32 window 的 WndProc 方法来处理消息,显然 the User32.GetMessage() does not return all messages.

在 WPF 中,您可以 add a hook to the HwndHost 处理 WndProc 消息。如何在不使用 WPF 的情况下在 .Net 6 中获取 WndProc 消息?


TL;DR: 本质上,无响应是由于您的代码 not 处理 Windows Message Pump 用于有问题的 window.


如您的行所述"You need to get the messages for the window and dispatch them accordingly"。即 GetMessageTranslateMessageDispatchMessage,都在 while 循环内,也称为 消息循环 .

I would like to somehow override or hook into the WndProc method of the Win32 window to handle messages, as apparently...

事情不是这样的。如果你的进程中创建了一个window,那么你有责任提供消息循环并采取行动。否则,您将体验到您现在所看到的一切——冻结 window。 “钩子” 不是正确的术语。

例如

User32.MSG msg;
while (User32.GetMessage(msg, hWnd, null, null) > 0)
{
    User32.TranslateMessage(msg);
    User32.DispatchMessage(msg);
}

我怀疑您的应用是 控制台应用,默认情况下不包含 Windows 消息泵。这就是为什么您不应该使用控制台应用程序项目向导来创建会公开 GUI 的内容的原因之一。虽然可以让控制台应用程序显示 GUI,但通常首先选择为 GUI 量身定制的项目向导会更容易。 [%]

In WPF you can add a hook to the HwndHost to handle the WndProc messages...

别忘了,WPF 使用 Direct3D 作为渲染表面,除了应用程序 window,还有 没有 child WIN32 windows 可言。这可以通过将 Spy++ 等工具指向 WPF 应用程序来轻松显示。当 Microsoft 设计 WPF 时,他们需要一种方法让 Microsoft UI Automation 与 child 元素交互。它这样做而不必担心 child windows.

% 脚注

说到将 GUIs 压缩到今天乍一看可能看起来像控制台应用程序的东西中,这就是 Windows 编写的 C 应用程序的处理方式] 在 C++ 之前。 C 应用程序可以在单个 main() 入口点启动并提供 window 全部服务,但是没有控制台 window 出现,因此控制台比较不完全正确。

C 应用程序没有很多指导。有些人可能没有 table 用于 图标、键盘加速器、字符串 tables 之类的资源,但它们仍然是 GUI 应用程序。

Microsoft Visual C++ 改变了 MFC 的东西(几年后又用 ATL、WTL)不同的 GUI 项目类型,特别是从开发人员那里封装了很多东西Message Pump代码除了提供默认图标、键盘快捷键、字符串tables.

因此,使用控制台项目向导制作 C# GUI 应用类似于 bare-bones C GUI 应用。

因此你需要你的消息泵。

这是 Program.cs 文件的完整内容,在我弄清楚如何子类化 User32.WNDCLASS 之后。该项目使用 .Net 6 和新 console template that uses implicit using statements and no explicit class definition or Main method. 不过,您仍然需要做两件事:

代码如下:

using PInvoke;

unsafe
{
    User32.WndProc myWndProc = CustomWndProc;
    string className = "MyClassName";
    fixed (char* namePointer = className)
    {
        var window = new User32.WNDCLASS();
        window.lpszClassName = namePointer;
        window.lpfnWndProc = myWndProc;
        var ret = User32.RegisterClass(ref window);
        if (ret != 0)
        {
            // do some error handling here...
        }

        var windowHandle = User32.CreateWindowEx(
                                            User32.WindowStylesEx.WS_EX_APPWINDOW,
                                            className,
                                            "Window Title",
                                            User32.WindowStyles.WS_OVERLAPPEDWINDOW |
                                            User32.WindowStyles.WS_VISIBLE,
                                            100,
                                            100,
                                            800,
                                            800,
                                            IntPtr.Zero,
                                            IntPtr.Zero,
                                            IntPtr.Zero,
                                            IntPtr.Zero);
        User32.MSG msg;
        while (User32.GetMessage(&msg, windowHandle, User32.WindowMessage.WM_NULL, User32.WindowMessage.WM_NULL) > 0)   // synchonous call, will block the thread till a message is received
        {
            User32.TranslateMessage(&msg);
            User32.DispatchMessage(&msg);
        }
    }

    static IntPtr CustomWndProc(IntPtr hWnd, User32.WindowMessage msg, void* wParam, void* lParam)
    {
        return User32.DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam);
    }
}

delegate User32.WndProc WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

编辑:我使用 PInvoke nuget 包添加了对 WndProc 消息的处理。