如何在 .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"。即 GetMessage
、TranslateMessage
、DispatchMessage
,都在 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.
不过,您仍然需要做两件事:
- 添加PInvoke Win32 nuget package
- 允许在您的项目中使用安全代码
代码如下:
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 消息的处理。
我正在使用 .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"。即 GetMessage
、TranslateMessage
、DispatchMessage
,都在 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. 不过,您仍然需要做两件事:
- 添加PInvoke Win32 nuget package
- 允许在您的项目中使用安全代码
代码如下:
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 消息的处理。