实现Win32消息循环并使用P/Invoke创建Window对象

本文关键字:Invoke 创建 Window 对象 消息 Win32 循环 实现 | 更新日期: 2023-09-27 18:21:16

我的主要目标是通过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;
}

实现Win32消息循环并使用P/Invoke创建Window对象

这是一个功率不足的事件循环。考虑使用类似MsgWaitForMultipleObjectsEx的内容,而不是GetMessage

无论如何,创建窗口需要先注册一个窗口类(RegisterClassEx),然后创建窗口(CreateWindow)。两者都不是特别困难。您需要调用DefWindowProc,而不是使用base.WndProc()

尝试直接在消息循环内处理所有消息将过于困难,这就是创建窗口过程的原因。对于您选择直接处理的任何消息,不要调用TranslateMessageDispatchMessage

你可能想看看这家伙是如何检测USB设备的:检测USB设备