实现 Win32 消息循环并使用 P/Invoke 创建 Window 对象
我的主要目标是纯粹使用能够处理的 P/Invoke 调用来实现正确的消息循环USB HID 事件。当然,它的功能应该与以下在 Windows 窗体 中运行良好的代码相同。这个 NativeWindow 后代接收事件:
public class Win32EventHandler : NativeWindow
{
public const int WM_DEVICECHANGE = 0x0219;
public Win32EventHandler()
{
this.CreateHandle(new CreateParams());
}
protected override void OnHandleChange()
{
base.OnHandleChange();
IntPtr handle = UsbHelper.RegisterForUsbEvents(this.Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
// Handle event
}
base.WndProc(ref m);
}
}
... 由这个事件循环提供支持:
Win32EventHandler handler = new Win32EventHandler();
var context = new ApplicationContext();
Application.Run(context);
// Other thread calls:
// context.ExitThread()
我发现实现事件循环相当容易:
while (true)
{
res = Win32.GetMessage(out msg, IntPtr.Zero, 0, 0);
if (res == 0)
{
break;
}
Win32.TranslateMessage(ref msg);
Win32.DispatchMessage(ref msg);
if (msg.message == WM_DEVICECHANGE)
{
// Handle event
}
}
但我不知道应该如何创建底层 Window 对象。 NativeWindow 类的实现对我来说似乎太复杂了。
这是我目前的解决方案:
public void CustomLoop()
{
string clsName = "Class";
string wndName = "Window";
Win32.WNDCLASSEX wndClassEx = new Win32.WNDCLASSEX();
wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);
wndClassEx.lpszClassName = clsName;
wndClassEx.lpfnWndProc = WndProc;
Win32.RegisterClassEx(ref wndClassEx);
IntPtr windowHandle = Win32.CreateWindowEx(0, clsName, wndName, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
IntPtr usbEventHandle = UsbHelper.RegisterForUsbEvents(windowHandle);
Win32.MSG msg;
sbyte res = 0;
while (true)
{
res = Win32.GetMessage(out msg, IntPtr.Zero, 0, 0);
if (res == 0)
{
break;
}
if (msg.message == WM.DEVICECHANGE)
{
// Handle event (does not fire)
}
else
{
Win32.TranslateMessage(ref msg);
Win32.DispatchMessage(ref msg);
}
}
Win32.DestroyWindow(windowHandle);
Win32.UnregisterClass(clsName, IntPtr.Zero);
}
[AllowReversePInvokeCalls]
private IntPtr WndProc(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
case WM.DEVICECHANGE:
// Handle event (fires)
break;
default:
return Win32.DefWindowProc(hWnd, msg, wParam, lParam);
}
return IntPtr.Zero;
}
My main goal is to implement a proper message loop purely with P/Invoke calls that is able to handle USB HID events. Definitely its functionality should be identical with the following code that works well in Windows Forms. This NativeWindow descendant receives the events:
public class Win32EventHandler : NativeWindow
{
public const int WM_DEVICECHANGE = 0x0219;
public Win32EventHandler()
{
this.CreateHandle(new CreateParams());
}
protected override void OnHandleChange()
{
base.OnHandleChange();
IntPtr handle = UsbHelper.RegisterForUsbEvents(this.Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
// Handle event
}
base.WndProc(ref m);
}
}
... powered by this event loop:
Win32EventHandler handler = new Win32EventHandler();
var context = new ApplicationContext();
Application.Run(context);
// Other thread calls:
// context.ExitThread()
I found out that implementing the event loop is rather easy:
while (true)
{
res = Win32.GetMessage(out msg, IntPtr.Zero, 0, 0);
if (res == 0)
{
break;
}
Win32.TranslateMessage(ref msg);
Win32.DispatchMessage(ref msg);
if (msg.message == WM_DEVICECHANGE)
{
// Handle event
}
}
But I have no idea how the underlying Window object should be created. The implementation of the NativeWindow class seems too complex for me.
This is my solution at the moment:
public void CustomLoop()
{
string clsName = "Class";
string wndName = "Window";
Win32.WNDCLASSEX wndClassEx = new Win32.WNDCLASSEX();
wndClassEx.cbSize = (uint)Marshal.SizeOf(wndClassEx);
wndClassEx.lpszClassName = clsName;
wndClassEx.lpfnWndProc = WndProc;
Win32.RegisterClassEx(ref wndClassEx);
IntPtr windowHandle = Win32.CreateWindowEx(0, clsName, wndName, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
IntPtr usbEventHandle = UsbHelper.RegisterForUsbEvents(windowHandle);
Win32.MSG msg;
sbyte res = 0;
while (true)
{
res = Win32.GetMessage(out msg, IntPtr.Zero, 0, 0);
if (res == 0)
{
break;
}
if (msg.message == WM.DEVICECHANGE)
{
// Handle event (does not fire)
}
else
{
Win32.TranslateMessage(ref msg);
Win32.DispatchMessage(ref msg);
}
}
Win32.DestroyWindow(windowHandle);
Win32.UnregisterClass(clsName, IntPtr.Zero);
}
[AllowReversePInvokeCalls]
private IntPtr WndProc(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
case WM.DEVICECHANGE:
// Handle event (fires)
break;
default:
return Win32.DefWindowProc(hWnd, msg, wParam, lParam);
}
return IntPtr.Zero;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可能想看看这个人如何检测 USB 设备:用于检测 USB 设备的 USB 库
You may want to check out how this guy detect USB devices: A USB Library to Detect USB Devices
这是一个非常低能的事件循环。考虑使用诸如
MsgWaitForMultipleObjectsEx
之类的东西来代替GetMessage
。不管怎样,创建一个窗口需要首先注册一个窗口类(
RegisterClassEx
),然后创建窗口(CreateWindow
)。两者都不是特别困难。您需要调用DefWindowProc
,而不是使用base.WndProc()
。尝试直接在消息循环内处理所有消息将非常困难,这就是创建窗口过程的原因。对于您选择直接处理的任何消息,请勿调用
TranslateMessage
或DispatchMessage
。That's an very under-powered event loop. Consider using something like
MsgWaitForMultipleObjectsEx
instead ofGetMessage
.Anyway, creating a window requires you to first register a window class (
RegisterClassEx
) and then create the window (CreateWindow
). Neither one is particularly difficult. And instead of usingbase.WndProc()
, you'll need to callDefWindowProc
.Trying to handle all messages directly inside the message loop is going to be overly difficult, that's why window procedures were created. And don't call
TranslateMessage
orDispatchMessage
for any message you choose to process directly.