Posted by: Zeeshan Amjad | September 10, 2009

Accessing Windows Procedure in WPF Application


Sometimes we need to perform some low level work, which has no or limited support in WPF. In that case we have multiple options. One of the obvious solution is to write a COM DLL in Visual C++ to perform all the low level work and then call that DLL in WPF application. Other solution is to directly call the windows API from the WPF application using PInvoke. One more method is using unsafe feature of C# or doing mix managed and unmanaged programming in Visual C++. Each method has its own advantages and disadvantages.

Here we are going to discuss how we can access the windows messages in WPF applications using Windows procedures. Those who has some experience of Windows SDK programming are familiar with Windows Procedure. Here is a little recap for rest of us.

In old days there is only one way to write a windows based program and there is only one language i.e. C language. Each windows based program perform the following steps.

1. Fill the structure name WNDCLASS or WNDCLASSEX

2. Register the windows class

3. Create Window

4. Implement message loop. Its is simple while loop (can also be a for loop)

5. Write a function and register it as a windows procedure in WNDCLASS or WNDCLASSEX structure.

This function has a huge switch case statement. What this function basically do is check the window messages (including command and notify messages from the child window) and perform some action base on it.

In WPF message loop is hidden inside the Run function of Dispatcher class. We already got some overview of it earlier. And Window procedure is break down into event handlers.

Now if we want to do the same thing in WPF application then we have to dig little bit deeper and perform some operation directly on low level data structure such as HWND (Handle of Window).

Just like other WPF things, there are more than one way to to get handle of window. Let’s take a look at both method one by one.

In first method we are going to use the PresentationSource class to get HwndSource object. HwndSource class implements the windows procedure and if we want to implement our own windows procedure then we have to add one hook using AddHook function of it. This is managed class, but it represents the unmanaged resources such as handle of window etc. therefore this class has Dispose function that will destroy the unmanaged objects. Dispose can be call from the same thread or from another thread. The only difference is when call with the same thread the unmanaged resources will be destroyed synchronously, however in case of other thread it will be asynchronous. This class is inherited by PresentationSource class, here is a class hierarchy of these classes.

WpfHwnd_01

Here one interesting property of PresentationCore class is CompositionTarget class. This is an abstract class and property will return one of its derived class. This class represent the display surface of the application. There are two classes inherited by this named “HwndTarget” and “VisualTarget” to perform some low level work. Here is a class diagram of CompositionTarget class.

WpfHwnd_03

Now lets put all the pieces together and make a simple WPF application that uses the Windows procedures. Here is the code of simple application using the windows procedure with the help of PresentationCore class.

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Interop;
  4: 
  5: public class MyWindow : Window
  6: {
  7:     private const int WM_LBUTTONDOWN = 0x0201;
  8:     private const int WM_RBUTTONDOWN = 0x0204;
  9: 
 10:     public MyWindow()
 11:     {
 12:         Title = "Windows Proc in WPF Application";
 13:         Width = 400;
 14:         Height = 300;
 15:     }
 16: 
 17:     protected override void OnSourceInitialized(EventArgs e)
 18:     {
 19:         base.OnSourceInitialized(e);
 20: 
 21:         HwndSource hwnd = (HwndSource)PresentationSource.FromVisual(this);
 22: 
 23:         if (hwnd != null)
 24:         {
 25:             hwnd.AddHook(new HwndSourceHook(WndProc));
 26:         }        
 27:     }
 28: 
 29:     protected IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
 30:     {
 31:         if (msg == WM_LBUTTONDOWN)
 32:         {
 33:             MessageBox.Show("Mouse Left Button Down", "From Window Proc", 
 34:                 MessageBoxButton.OK, MessageBoxImage.Information);
 35:         }
 36:         else if (msg == WM_RBUTTONDOWN)
 37:         {
 38:             MessageBox.Show("Mouse Right Button Down", "From Window Proc",
 39:                 MessageBoxButton.OK, MessageBoxImage.Information);
 40:         }
 41: 
 42:         return IntPtr.Zero;
 43:     }
 44: }
 45: 
 46: public class wpf
 47: {
 48:     [STAThread]
 49:     public static void Main()
 50:     {
 51:         MyWindow win = new MyWindow();
 52: 
 53:         Application app = new Application();
 54:         app.Run(win);
 55:     }
 56: }
 57: 

The output of this program is one simple window. But when user click the left mouse button or right mouse button inside the window then it will display the message box to display the appropriate message on it.

Here is the output of this program.

WpfHwnd_04

Now lets take a look at the other approach. Other method is exactly teh same except now we are getting handle of window with WindowInteropHelper class. Let’s take a look at this class first. This is very simple class to perform some low level programming from WPF. Here is its class diagram.

WpfHwnd_02

We get IntPtr instead of HwndSource therefore we need to convert it from IntPtr to HwndSource. Here is a simple code to perform this.

  1: WindowInteropHelper wih = new WindowInteropHelper(this);
  2: IntPtr hwnd = wih.Handle;
  3: HwndSource hsource = HwndSource.FromHwnd(hwnd);

Rest of the code is exactly same. Now take a look at the whole program using this method.

  1: using System;
  2: using System.Windows;
  3: using System.Windows.Interop;
  4: 
  5: public class MyWindow : Window
  6: {
  7:     private const int WM_LBUTTONDOWN = 0x0201;
  8:     private const int WM_RBUTTONDOWN = 0x0204;
  9: 
 10:     public MyWindow()
 11:     {
 12:         Title = "Windows Proc in WPF Application";
 13:         Width = 400;
 14:         Height = 300;
 15:     }
 16: 
 17:     protected override void OnSourceInitialized(EventArgs e)
 18:     {
 19:         base.OnSourceInitialized(e);
 20: 
 21:         WindowInteropHelper wih = new WindowInteropHelper(this);
 22:         IntPtr hwnd = wih.Handle;
 23:         HwndSource hsource = HwndSource.FromHwnd(hwnd);
 24: 
 25:         if (hwnd != null)
 26:         {
 27:             hsource.AddHook(new HwndSourceHook(WndProc));
 28:         }
 29:     }
 30: 
 31:     protected IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
 32:     {
 33:         if (msg == WM_LBUTTONDOWN)
 34:         {
 35:             MessageBox.Show("Mouse Left Button Down", "From Window Proc",
 36:                 MessageBoxButton.OK, MessageBoxImage.Information);
 37:         }
 38:         else if (msg == WM_RBUTTONDOWN)
 39:         {
 40:             MessageBox.Show("Mouse Right Button Down", "From Window Proc",
 41:                 MessageBoxButton.OK, MessageBoxImage.Information);
 42:         }
 43: 
 44:         return IntPtr.Zero;
 45:     }
 46: }
 47: 
 48: public class wpf
 49: {
 50:     [STAThread]
 51:     public static void Main()
 52:     {
 53:         MyWindow win = new MyWindow();
 54: 
 55:         Application app = new Application();
 56:         app.Run(win);
 57:     }
 58: }
 59: 

The output of this program is exactly the same as the previous one.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: