WndProc 重载 + 非托管 DLL 包装器:更好的方法?

WndProc Overloading + Unmanaged DLL Wrapper: Better way?

(顺便说一句,这是 C# .NET 4.5)

我有一些与某些硬件对话的非托管 DLL。我包装了一堆代码并得到了一些简单的东西,作为一个 class 对象,我可以在 WinForm 中创建它。

    private AvaSpec AS = new AvaSpec();

    public AvaSpec_Form()
    {
        InitializeComponent();

        AS.SpectrumMeasuredEvent += (se, ev) => { SpectrumMeasured(ev); };

        AS.Init(this.Handle);
        AS.Activate();

        // configure as desired
        // AS.l_PrepareMeasData.m_IntegrationDelay = 0;

        if (AS.DeviceList.Count > 0)
        {
            AS.Start();
        }
    }

但是,DLL 依赖于通过 WndProc 接收消息。我能想到的最好的方法是在 Form:

上重载 WndProc 方法
    protected override void WndProc(ref Message m)
    {
        // catch WndProc messages that AvaSpec defines as its own
        if (m.Msg == AvaSpec.WM_MEAS_READY || 
                m.Msg == AvaSpec.WM_APP || 
                m.Msg == AvaSpec.WM_DBG_INFOAs || 
                m.Msg == AvaSpec.WM_DEVICE_RESET )
        {
            AS.WndProcMessageReceived(ref m);
        }

        // else pass message on to default message handler
        base.WndProc(ref m);
    }

如何在 class 定义中以某种方式隐藏此重载,以便不需要将重载方法添加到表单本身?有一些关于 IMessageFilter 接口的讨论,但它看起来仍然需要在表单中添加一些代码来添加过滤器。关于如何使它更优雅的任何想法?

您可以创建一个隐藏的无模式 "form"/window,然后在对 'AS.Init'.

的调用中使用它的 .Handle

通过使用单独的 "window" 而不是搭载到主应用程序 window,它提供了更好的封装。

例如,如果将来您需要支持同时处理多个设备...那么 "separate" windows 可以很好地分离不同设备的消息。

您的 hardware/device 处理代码可能使用 wParam 或 lParam 来识别 "device id"...但更有可能将它们用于其他用途,并依赖于 "window destination"作为区分符。

然后让主应用 UI 线程消息泵...自动将消息发送到您创建的 windows。

在 "window" 的消息处理代码中,您将处理消息,其中包括特殊的私人注册消息,例如 WM_DBG_INFOAs 等...然后您将其转发回AvaSpec 通过 WndProcMessageReceived.

如果 AvaSpec class 依赖于您及时处理这些消息,那么您可能需要探索创建多个 UI 线程。

如果您的主应用 UI 线程过载,或者 "busy" 正在处理其他消息,例如调整大小时、移动 window 等,则可能需要这样做

通过单独的 UI 线程为您的隐藏 "device" window 发送消息,那么它可能会为您的 "device" 提供更好的响应。

注意:多个 UI 线程是一个高级主题,有一些陷阱,但基本上它涉及创建一个线程,告诉它使用 STA(单线程单元),创建你的 window 表单,然后通常将 Application.Run 与该表单一起使用以引起消息抽取。

好的,我根据 Colin Smith 的提示弄明白了。

您从 NativeWindow 派生了 class:

https://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow(v=vs.110).aspx

然后将父(窗体)句柄(通过某些初始化传递)分配给 NativeWindow 提供给 class 对象的句柄。然后,您可以直接在对象中重载 WndProc 方法。

// object definition

public class AvaSpec : NativeWindow
{
    protected override void WndProc(ref Message m)
    {
        // catch WndProc messages that AvaSpec defines as its own
        if (m.Msg == AvaSpec.WM_MEAS_READY || 
            m.Msg == AvaSpec.WM_APP || 
            m.Msg == AvaSpec.WM_DBG_INFOAs || 
            m.Msg == AvaSpec.WM_DEVICE_RESET)
        {
            WndProcMessageReceived(ref m);
        }

        // Call base WndProc for default handling
        base.WndProc(ref m);
    }

...(剪断)

    public void Init(IntPtr parentHandle)
    {
        this.AssignHandle(parentHandle);

...(剪断)

并像这样使用它(通过一些初始化传递句柄指针):

// WinForm definition

public partial class AvaSpec_X : Form
{
    private AvaSpec AS = new AvaSpec();

    public AvaSpec_X()
    {
        InitializeComponent();

        AS.SpectrumMeasuredEvent += (se, ev) => { SpectrumMeasured(ev); };

        AS.Init(this.Handle);
        AS.Activate();

        // configure as desired
        //AS.l_PrepareMeasData.m_IntegrationDelay = 0;

        if (AS.DeviceList.Count > 0)
        {
            AS.Start();
        }
    }

...(剪断)