【问题标题】:Implementing a Win32 message loop and creating a Window object with P/Invoke实现 Win32 消息循环并使用 P/Invoke 创建 Window 对象
【发布时间】:2012-02-17 08:21:00
【问题描述】:

我的主要目标是完全使用能够处理 USB HID 事件的 P/Invoke 调用来实现正确的消息循环。当然,它的功能应该与以下在Windows Forms 中运行良好的代码相同。这个 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;
}

【问题讨论】:

  • 出于好奇,如果您有工作代码,为什么要以不同的方式重写它?
  • 我想用 P/Invoke 在 Silverlight 5 中捕捉这些事件。有充分的理由,它完全缺乏 Win32 特定的东西。
  • 您从 pinvoke.net 获得 p/invoke 声明?

标签: c# .net silverlight pinvoke message-loop


【解决方案1】:

你可能想看看这个家伙是如何检测 USB 设备的:A USB Library to Detect USB Devices

【讨论】:

  • 这不是我错过的信息。我知道如何监听 USB 事件。提供的样本与我完全相同。我的问题是我无法创建像 NativeWindow 类这样的 Win32 Window 对象。这将是必不可少的,因为 Silverlight 不公开底层 Window 对象的句柄,也不提供任何方法来挂钩事件循环。
【解决方案2】:

这是一个功能非常不足的事件循环。考虑使用 MsgWaitForMultipleObjectsEx 之类的东西,而不是 GetMessage

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

试图直接在消息循环中处理所有消息将会非常困难,这就是创建窗口过程的原因。对于您选择直接处理的任何消息,请勿致电 TranslateMessageDispatchMessage

【讨论】:

  • 这是我正在做的,但它似乎不起作用。但是现在我发现USB事件调用了lpfnWndProc回调方法(RegisterClassEx),但是GetMessage调用没有返回。这是正确的行为吗?
  • @tamasf:你检查所有调用是否有错误?如果您没有正确处理消息(通过将它们传递给DefWindowProc),您的CreateWindow 调用将返回NULLRegisterDeviceNotification 将失败,并且您将不会收到消息。
  • @tamasf:您可能会努力让Raymond Chen's skeleton Win32 app 启动并运行,首先在 C 中,然后在 C# 中使用 P/Invoke。
  • @tamasf:我没有看到任何错误检查。您是否在调试器中单步执行以确保返回值一切正常?此外,“类”是一个糟糕的窗口类名称,它必须是唯一的。使用 GUID,或您的姓名,或其他懒惰的开发人员不会使用的东西。此外,所有窗口样式的零都不太可能很好。
  • 代码现在可以运行 (WndProc),我唯一担心的是在引发 USB 事件时不会返回的 GetMessage 调用。应该是?顺便说一句,感谢您的建议:我将通过错误检查完成代码。该窗口从未显示(ShowWindow),所以我没有找到任何合适的样式标志。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-31
  • 1970-01-01
  • 2011-05-19
  • 2020-03-29
  • 1970-01-01
相关资源
最近更新 更多