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



(顺便说一句,这是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 接收消息。我能弄清楚的最好的方法是在窗体上重载 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接口的讨论,但它看起来仍然需要表单中的一些代码来添加过滤器。关于如何使其更优雅的任何想法?

好的,我根据科林史密斯的提示想通了。

您可以从 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);

。(截图)

并使用它(通过一些 init 传递句柄指针),如下所示:

// 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();
}
}

。(截图)

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

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

例如,如果将来您需要支持同时处理多个设备...然后,"单独"窗口将为不同设备提供良好的消息分离。

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

然后让主要应用程序UI线程消息泵...自动将消息调度到您创建的窗口。

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

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

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

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

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

最新更新