The System.Windows.Forms namespace is a wrapper around the native Win32 GUI functionality provided by the operating system. The .NET Framework does a pretty good job at hiding the specifics of the Win32 APIs from managed developers, providing them with a more object orientated programming model. However the managed wrappers do not provide access to all features present in the underlying OS. This is especially true of the .NET Compact Framework. One technique to access native functionality which is not supported by the .NET Compact Framework is to send window messages. This is the subject of this blog entry.
Requirements
This blog entry is specific to .NET Compact Framework v2.0 or higher. Although most of the concepts are usable in .NET Compact Framework v1.0, you will need to do some additional work, since some of the properties utilised in the samples are not directly available in the older version of the .NET Compact Framework.
To get the code samples within this blog entry to compile within your projects, you must:
- Include a reference to the Microsoft.WindowsCE.Forms assembly
- Include a “using Microsoft.WindowsCE.Forms” statement at the top of your source code file
What is a Window and what is a Message?
The Win32 GUI platform is built around the concept of “windows”. A window is a rectangular section of the screen. Any window can also have additional child windows, which are windows contained within the rectangular space allocated to their parent.
The concept of a window is an overloaded term. The Win32 Operating System APIs not only consider Forms to be windows, but also controls you commonly place on a form such as TextBoxes or Labels. What differentiates the behavior and look and feel of these different windows is their “window class”.
Within the Win32 GUI API windows communicate with each other via the concept of messages. Messages are sent to a specific window to inform them whenever events such as typing, or tapping on the screen occur. Other window messages are of a more house keeping nature and indicate that a window should repaint itself etc.
For the purposes of the Win32 GUI APIs a windows message has the following properties:
- Message Type – used to differentiate between different types of messages
- WParam – a value, the meaning of which depends upon the type of message
- LParam – essentially the same as WParam, another message type dependent field
Within the .NET Compact Framework, a window message is represented by the Microsoft.WindowsCE.Forms.Message class.
Finding a Window Handle
In order to send a message to a window you must obtain it’s handle. A handle is simply a special value which uniquely identifies a particular window to the native Win32 GUI APIs.
In many cases you can easily obtain a window handle for a control within your application by querying the Handle property of the desired control. This property is available on all the GUI controls (including forms), because they all eventually derive from the Control base class.
The following example demonstrates how you may obtain the window handle of a textbox placed onto your form:
private void button1_Click(object sender, EventArgs e) { // Obtain the window handle for a textbox control // called 'textBox1' which we have placed onto the form IntPtr hwnd = textBox1.Handle; }
By convention window handles are typically referred to as HWNDs, this is Hungarian notation for “Handle to a Window” and can be traced back to the days when C developers prefixed their variables with a description of the variable’s data type.
Sometimes you need to send a message to a window created by another application. In this scenario you do not have a Control instance to query the Handle property. In this case you need to ask the operating system to search for the window given it’s name.
The .NET Compact Framework does not support this, so you will need to Platform Invoke a native Win32 API called FindWindow, as the following example demonstrates:
using System.Runtime.InteropServices; [DllImport("coredll.dll")] private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); private void button1_Click(object sender, EventArgs e) { // Find a window with the name "Test Application" IntPtr hwnd = FindWindow(null, "Test Application"); }
Fabien Decret mentions in his blog posting titled “Find a Window Handle” how you can use the Remote Spy utility to determine the name and window class of a particular window.
Sending a Message
To send a message to a window we simply create an instance of the Message class and configure it’s properties to the desired values. We then pass the message along to the MessageWindow.SendMessage static method to have it sent.
The following example demonstrates how to send a WM_CLOSE message to the File Explorer application, which will have the effect of closing it.
// Define the window message we want to send const int WM_CLOSE = 0x10; // Find the window we want to send the message to IntPtr hWnd = FindWindow(null, "File Explorer"); // If the window was found, ask the application to close if (hWnd != IntPtr.Zero) { // Create a WM_CLOSE message (it doesn't use the lParam // and wParam fields so we set them to zero) and send the // message to the window Message msg = Message.Create(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); MessageWindow.SendMessage(ref msg); }
The documentation for the WM_CLOSE window message can be found on MSDN. This documentation explains the interpretation of the lParam and wParam parameters, as well as outlining at the bottom of the page, which header file we need to look in to obtain the value of WM_CLOSE (i.e. 0×10).
There is also a MessageWindow.PostMessage method, which is utilised the same way as SendMessage except that unlike SendMessage it does not wait for the message to be processed by the window it is sent to before returning.
Receiving a Message
On the desktop, the Control class provides a WndProc method which can be overridden to process any window message sent to the control. The Compact Framework however does not provide the WndProc method for us to override, so there is no (easy) way to see window messages sent to a control.
The .NET Compact Framework instead has a special class called MessageWindow which enables the creation of a hidden window that provides a virtual WndProc method which can be overridden to process any window messages sent to it.
To make use of this class applications must derive a class from MessageWindow and then override the WndProc method so that messages sent to the window can be processed.
The following code sample shows how to derive a class from the MessageWindow base class:
public class MyMessageWindow : MessageWindow { // The window message type we want to process public static readonly int WM_USER = 0x400; // This method gets called for every window message this // window receives. protected override void WndProc (ref Message m) { // Check if it is a message we want to handle // based upon it's message type if (m.Msg == WM_USER) { // This is a message we recognise, so lets // process it. In this case we'll display a message // box MessageBox.Show("Hello - i received a window message"); } base.WndProc (ref m); } }
Because this is a managed application, there is no definitions available for the standard window messages such as WM_USER. A glance at the MSDN Documentation for WM_USER will confirm that the definition can be found within the C header file winuser.h, managed developers need to duplicate this definition in their own applications, as the code sample demonstrates. WM_USER is a good message number to utilise, since Microsoft defines this message number as the starting point for user defined messages.
Once we have defined our MessageWindow subclass we simply need to create an instance of the class to create the hidden window. The class has an Hwnd property which provides the window handle we need to send messages to in order for them to be processed by this window.
The following code sample demonstrates how to create an instance of the MyMessageWindow class, and then send a window message to it.
// Create the hidden window MessageWindow msgWindow = new MyMessageWindow(); // Send a message to it Message msg = Message.Create(msgWindow.Hwnd, MyMessageWindow.WM_USER, IntPtr.Zero, IntPtr.Zero); MessageWindow.SendMessage(ref msg);
The previous code sample is not a typical use case of a message window. Typically the window will be created by one part of an application, and a message will be sent to the window from an unrelated part of the application (or even another application entirely). This leads to a problem, since the code which sends the window message will typically not have access to the window handle (Hwnd property value) to know where to send the message.
In this case you may like to set your MessageWindow’s Text property to something uniquely identifiable as your application. You can then use the FindWindow approach mentioned previously to find the window handle by passing in the same string.
Practical Uses
Sending window messages can be utilised for a number of purposes, but they are mainly utilised to interface to external applications. The following references are to real life situations where sending window messages is useful:
- Alex Feinman demonstrates how to work around a textbox selection issue by accessing functionality not exposed by the .NET Compact Framework textbox control.
- Chris Tacke demonstrates how to control a bluetooth radio when presented with no API to programmatically control it.
- Alex Yakhnin demonstrates how to create a system tray icon in managed code. In other words dealing with an operating system API which expects to communicate with you via window messages.
- Daniel Moth discusses the possibility of using Window Messages as a form of Inter Process Communication.
Sample Application
I have put together a small sample application to demonstrate some of these techniques. the easiest way to play with the demo is to open up the “SenderApplication” and “SecondApplication” projects into two instances of Visual Studio 2005. You will then be able to follow the “demo script” below:
- Start the “SecondApplication” in the debugger (press F5)
- Start the “SenderApplication” in the debugger (press F5 – also ensure you select the same emulator/device)
- Click the “Click another button” button and notice how the application behaves as if the “Show a Message Box” button was pressed. This is because a button click window message is being sent to the message box button whenever the first button is clicked.
- Click the “Display message in another app” button. Notice how the second application pops up and displays a message box stating the current state of the checkbox in the first application. This is because the “SenderApplication” has sent a window message to the MessageWindow created by the “SecondApplication”.
- Dismiss the message box and minimise the “SecondApplication” to return to the “SenderApplication”.
- Change the state of the checkbox and click the “Display message in another app” button a couple of times to see how the two applications are communicating the state of the checkbox.
- Press the “Close other app” button, and notice that the “SecondApplication” application shuts down (which will cause Visual Studio to stop debugging that project).
This blog entry neatly covers some of the background theory on something I have been working on quietly in the past few days, hopefully early next week I’ll have an interesting application to blog about….
interesting post. i never knew netcf supported sending windows messages like this in managed code! :) although i have encountered the Message class many times before :P
cheers!
Nice idea, gracias!
hello all, can anyone tell how to get a windows message sents for other applications?
Ex: i want to veiw the message from media player and to control this in my application!
when the user tap stop on media player my app do something!
can anyone help me?