Introduction
Sometimes, you need to send data between two processes. One traditional way to do this has been to use SendMessage
with the message WM_COPYDATA passing a COPYDATASTRUCT as a parameter.
From Windows Vista, the message WM_COPYDATA
is blocked by the User Interface Privilege Isolation (UIPI) message filter... This tip shows how to create the necessary code and data structures and allow the message to come through.
Background
Just recently, I wrote a small application that is used to print front and back of an ID card on the same paper. After a bit of testing of how to get the scanned pages into the application and also make it as user friendly as possible, I opted to use the AutoScan button of the scanner, a CanoScan 9000F. This scanner can be configured to start any application when scan has been executed. The application is started and the filename of the scanned image is passed as a command line argument.
This works well, except the scanner driver launches one instance of the application per press of the button, and this was not what I wanted.
The first problem to solve was to prevent the new instance from running and the next problem was to send the new filename to the running instance. This was more work than I expected.
Using the Code
The example code is written as a Windows Forms application. The only thing the application does is to take a command line argument and add it to a RichTextBox
.
Note
I have only tested this application using Windows 8.1 and .NET 4.5
Platform Invoke
As some platform invoke (pinvoke) technology is involved, I will start with referring to the site pinvoke.net. Here, you can find many C# signatures for Win32
methods.
In order to keep the pinvoke stuff in one place, I created a helper class called MessageHandler
which is shown below:
using System;
using System.Runtime.InteropServices;
namespace SendMessageDemo
{
internal static class NativeMethods
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr HWND_BROADCAST = new IntPtr(0xffff);
public static uint WM_COPYDATA = 0x004A;
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
public enum MessageFilterInfo : uint
{
None = 0,
AlreadyAllowed = 1,
AlreadyDisAllowed = 2,
AllowedHigher = 3
}
public enum ChangeWindowMessageFilterExAction : uint
{
Reset = 0,
Allow = 1,
DisAllow = 2
}
[StructLayout(LayoutKind.Sequential)]
public struct CHANGEFILTERSTRUCT
{
public uint size;
public MessageFilterInfo info;
}
[DllImport("user32.dll", SetLastError = true)]
public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint msg,
ChangeWindowMessageFilterExAction action, ref CHANGEFILTERSTRUCT changeInfo);
}
}
Good old MSDN is filled with information about the methods used herein, so here is a list of references to the different methods and structures.
I strongly recommend you read the information on MSDN if you run into trouble.
One Instance Application
In my case, I needed to make sure that only one instance of the application was launched and after some searching, the solution was to use the Mutex
class, and more specifically once named Mutex.
I also needed to get the filename sent to the application, so I added code for that too. Below is the modified main
function in the file Program.cs.
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string fileName = null;
if (args.Length == 1)
fileName = args[0];
bool isNewInstance;
Mutex singleMutex = new Mutex(true, "MyAppMutex", out isNewInstance);
if (isNewInstance)
{
Form1 frm = new Form1(fileName);
Application.Run(frm);
}
else
{
MessageBox.Show(fileName, "Received File Name",
MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
So that took care of the first problem.
Transferring the File Name
The next problem to solve was how to get the file name of the second scanned image into the running application. There are quite a few ways to solve this, for example named pipes, sockets or writing data to a file or the registry. In my opinion, this creates a lot of overhead for the simple task of sending a string
from one application to another, so I wanted something more straightforward.
I started out by using SendMessage
and my own defined message, but ran into various memory access violations (of course). This is because SendMessage
only passes a pointer to a memory location. However, the pointer doesn't point to a physical memory location, but a virtual and two applications will have two different virtual memory spaces. The result is that the pointer will have no useful meaning for the receiving application. (Before I have mostly used SendMessage
within in the same application where this is not an issue)
The solution is to use the message WM_COPYDATA
as this message is designed to transfer data between processes. One caveat for this message is that on Windows Vista and later, it is blocked by default by the User Interface Privilege Isolation (UIPI) message filter. (I have only tested this application on Windows 8.1)
Enabling WM_COPYDATA
It is simple enough to unblock the message with the code below:
private void Form1_Load(object sender, EventArgs e)
{
NativeMethods.CHANGEFILTERSTRUCT changeFilter = new NativeMethods.CHANGEFILTERSTRUCT();
changeFilter.size = (uint)Marshal.SizeOf(changeFilter);
changeFilter.info = 0;
if (!NativeMethods.ChangeWindowMessageFilterEx
(this.Handle, NativeMethods.WM_COPYDATA,
NativeMethods.ChangeWindowMessageFilterExAction.Allow, ref changeFilter))
{
int error = Marshal.GetLastWin32Error();
MessageBox.Show(String.Format("The error {0} occurred.", error));
}
}
The most likely error message would be ERROR_ACCESS_DENIED
if the process security level is too low.
Sending the Message
In order to send the message, a handle to the existing window has to be available. This is done with the method FindWindow
using the title of the window as the parameter.
We also have to create an instance of the COPYDATASTRUCT
structure that will be used to hold the data to send.
- The member
dwData
can, for example, be used to identify the type of data sent.
- The member
cbData
contains the length of the data in lpData
.
- The member
lpData
is a pointer to the data. In this case, a string
.
Below is the finalized else
part of the main
function in Program.cs.
else
{
string windowTitle = "SendMessage Demo";
IntPtr ptrWnd = MessageHandler.FindWindow(null, windowTitle);
if (ptrWnd == IntPtr.Zero)
{
MessageBox.Show(String.Format("No window found with the title {0}.", windowTitle),
"SendMessage Demo", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
IntPtr ptrCopyData = IntPtr.Zero;
try
{
NativeMethods.COPYDATASTRUCT copyData = new NativeMethods.COPYDATASTRUCT();
copyData.dwData = new IntPtr(2); copyData.cbData = fileName.Length + 1; copyData.lpData = Marshal.StringToHGlobalAnsi(fileName);
ptrCopyData = Marshal.AllocCoTaskMem(Marshal.SizeOf(copyData));
Marshal.StructureToPtr(copyData, ptrCopyData, false);
NativeMethods.SendMessage(ptrWnd, MessageHandler.WM_COPYDATA, IntPtr.Zero, ptrCopyData);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "SendMessage Demo",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
if (ptrCopyData != IntPtr.Zero)
Marshal.FreeCoTaskMem(ptrCopyData);
}
}
}
Implementing the Message Receiver
The last step is to implement the code used to receive the message and the data. This is done by overriding the method WndProc
in the Form
class.
This code is simple enough when just sending a string
.
protected override void WndProc(ref Message m)
{
if (m.Msg == MessageHandler.WM_COPYDATA)
{
NativeMethods.COPYDATASTRUCT copyData =
(NativeMethods.COPYDATASTRUCT)Marshal.PtrToStructure
(m.LParam, typeof(NativeMethods.COPYDATASTRUCT));
int dataType = (int)copyData.dwData;
if (dataType == 2)
{
string fileName = Marshal.PtrToStringAnsi(copyData.lpData);
richTextBox1.AppendText(fileName);
richTextBox1.AppendText("\r\n");
}
else
{
MessageBox.Show(String.Format("Unrecognized data type = {0}.",
dataType), "SendMessageDemo", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
base.WndProc(ref m);
}
}
The receiving application should NOT free any memory. This should be done by the sender.
Points of Interest
In my case, I only needed to send a string
, but it is of course possible to send more complex data as well. In order to do this, you can create a structure in the same fashion as the COPYDATASTRUCT
structure and use Marshal.StructureToPtr on the sending side and Marshal.PtrToStructure on the receiving side.
Mike Barthold pointed out that I didn't follow Microsoft Code Compliance rules regarding the class containing the native methods. See CA1060: Move P/Invokes to NativeMethods class. It is good to learn something new everyday.
History
- 2015-08-11 First release
- 2015-08-12 Second release. Changed the naming convention of the class containing P/Invoke methods to NativeMethods
- 2015-08-12 Two bugs fixed in Program.cs:
if (ptrWnd == null)
corrected to if (ptrWnd == IntPtr.Zero)
String.Format("No window found with the title", windowTitle)
corrected to
String.Format("No window found with the title '{0}'.", windowTitle)