Search code examples
c#dllfilterunmanagedwndproc

WndProc Overloading + Unmanaged DLL Wrapper: Better way?


(BTW this is C# .NET 4.5)

I have some unmanaged DLL that talks to some hardware. I wrap a bunch of code and get something simple, as a class object, that I can create in a 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();
        }
    }

However, the DLL relies on receiving messages through WndProc. The best way I could figure out to do this is to overload the WndProc method on the Form:

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

How can I hide this overload somehow in the class definition so that the overload method does not need to be added to the Form itself? There is some talk of the IMessageFilter interface, but it still looks to require some code in the form to add the filter. Any ideas on how to make this more elegant?


Solution

  • Ok I figured it out based on Colin Smith's hints.

    You derive your class from NativeWindow:

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

    Then assign the parent (form) Handle (that you pass by some initialization) to the Handle that NativeWindow provides to the class object. Then, you can overload the WndProc method directly in the object.

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

    ...(snip)

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

    ...(snip)

    and use it (pass handle pointer via some init) like so:

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

    ...(snip)