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();
}
}
...(剪断)
(顺便说一句,这是 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();
}
}
...(剪断)