Using a custom WndProc in WPF apps


A current project at work needs to be able to respond to USB ‘thumb’ drives being inserted and removed. In Windows, such notifications are handled by processing the WM_DEVICECHANGE message. Using Windows Forms this is reasonably straightforward: the Form class has a protected WndProc method that can be overridden, like so:

protected override void WndProc(ref Message m)
{
    if (m.Msg == WM_DEVICECHANGE)
    {
        // Handle WM_DEVICECHANGE...
    }

    base.WndProc(ref m);
}

Unfortunately the same technique cannot be used in WPF applications. The spiritual equivalent of the Form class in WPF is Window, which has no WndProc method to override. So what to do?

Turns out that there is a helper class in the System.Windows.Interop namespace called HwndSource that can be used to hook into the window procedure (amongst other things). From the MSDN documentation:

The HwndSource class implements its own window procedure.  This window procedure is used to process important window messages, such as those related to layout, rendering, and input.  However, you can also hook the window procedure for your own use.  You can specify your own hook during construction by setting the HwndSourceParameters.HwndSourceHook property, or you can also use AddHook and RemoveHook to add and remove hooks after the window has been created.

First we need a HwndSource instance, so that we can call its AddHook method. HwndSource is derived from PresentationSource; using the static PresentationSource.FromVisual method we can obtain a PresentationSource from a specified WPF Window, and cast it as follows:

HwndSource source = PresentationSource.FromVisual(window) as HwndSource;
// 'window' is a reference to a WPF Window instance

Now we have a HwndSource instance, we can call its AddHook method. This takes a single parameter, a HwndSourceHook delegate, the signature for which is:

public delegate IntPtr HwndSourceHook(
    IntPtr hwnd,
    int msg,
    IntPtr wParam,
    IntPtr lParam,
    ref bool handled)

Putting this all together, we end-up with the following:

using System;
using System.Windows;
using System.Windows.Interop;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
            source.AddHook(WndProc);
        }

        IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Handle messages...

            return IntPtr.Zero;
        }
    }
}

Here we have a derived Window class called Window1. We’ve overridden the OnSourceInitialized method to create our HWndSource object and call its AddHook method (it’s not possible to do this in the constructor as the window’s handle has not been created at that point, hence there will be no HwndSource).

Our window procedure method is called WndProc (no points for originality), and matches the HwndSourceHook delegate signature, meaning that we can just pass that method name to AddHook.

At this point, we’ve implemented the WPF equivalent of overriding a Form’s WndProc method. All that’s left to do now is to handle the relevant Windows messages as per our application’s requirements. In a future post I’ll detail how to implement those USB drive notifications that were mentioned at the beginning of this article.

,

Comments are closed.