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:
view sourceprint?
01.protected override void WndProc(ref Message m)
02.{
03.    if (m.Msg == WM_DEVICECHANGE)
04.    {
05.        // Handle WM_DEVICECHANGE…
06.    }
07.
08.    base.WndProc(ref m);
09.}


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:
view sourceprint?
1.HwndSource source = PresentationSource.FromVisual(window) as HwndSource;
2.// ‘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:
view sourceprint?
1.public delegate IntPtr HwndSourceHook(
2.    IntPtr hwnd,
3.    int msg,
4.    IntPtr wParam,
5.    IntPtr lParam,
6.    ref bool handled)


Putting this all together, we end-up with the following:
view sourceprint?
01.using System;
02.using System.Windows;
03.using System.Windows.Interop;
04.
05.namespace WpfApplication1
06.{
07.    public partial class Window1 : Window
08.    {
09.        public Window1()
10.        {
11.            InitializeComponent();
12.        }
13.
14.        protected override void OnSourceInitialized(EventArgs e)
15.        {
16.            base.OnSourceInitialized(e);
17.            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
18.            source.AddHook(WndProc);
19.        }
20.
21.        IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
22.        {
23.            // Handle messages…
24.
25.            return IntPtr.Zero;
26.        }
27.    }
28.}

历史博文

标签:,
十月 17, 2009 at 2:00 上午 by yippee 11 次
Category: Info
Tags: ,