【问题标题】:WndProc Overloading + Unmanaged DLL Wrapper: Better way?WndProc 重载 + 非托管 DLL 包装器:更好的方法?
【发布时间】:2017-09-08 12:00:32
【问题描述】:

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

我有一些与某些硬件通信的非托管 DLL。我包装了一堆代码,得到了一些简单的东西,作为一个类对象,我可以在 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);
    }

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

【问题讨论】:

  • 没有什么可隐藏的,它与您覆盖的原始 Form.WndProc() 一样可见并受到保护。
  • 顺便说一句,我尝试了 IMessageFilter 接口,它可以工作,直到我移动表单,并且由于某种原因消息停止流向对象...?

标签: c# dll filter unmanaged wndproc


【解决方案1】:

好的,我根据 Colin Smith 的提示想通了。

你从 NativeWindow 派生你的类:

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

然后将父(表单)句柄(通过一些初始化传递)分配给 NativeWindow 提供给类对象的句柄。然后,您可以直接在对象中重载 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();
        }
    }

...(截图)

【讨论】:

    【解决方案2】:

    您可以创建一个隐藏的无模式“表单”/窗口,然后在对 'AS.Init' 的调用中使用其 .Handle。

    通过使用单独的“窗口”而不是捎带到主应用程序窗口,它提供了更好的封装。

    例如,如果将来您需要支持同时处理多个设备……那么“独立”窗口可以很好地分离不同设备的消息。

    您的硬件/设备处理代码可能使用 wParam 或 lParam 来识别“设备 id”...但更有可能将它们用于其他用途,并依赖“窗口目标”作为区分符。

    然后让主应用程序 UI 线程消息泵...自动向您创建的窗口发送消息。

    在该“窗口”的消息处理代码中,您将处理消息,其中包括特殊的私人注册消息,例如 WM_DBG_INFOA 等……然后通过WndProcMessageReceived 转发回AvaSpec。

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

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

    通过有一个单独的 UI 线程为隐藏的“设备”窗口发送消息,那么它可能会为您的“设备”提供更好的响应。

    注意:多UI线程是一个高级话题,有一些陷阱,但基本上它涉及创建一个线程,告诉它使用STA(单线程单元),创建你的窗口窗体,然后通常使用@ 987654326@ 使用该表单会导致消息泵送。

    【讨论】:

      猜你喜欢
      • 2011-10-30
      • 2020-08-30
      • 1970-01-01
      • 2011-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多