Use Wm_Copydata to Send Data Between Processes

Use WM_COPYDATA to send data between processes

For an example of how to use the message, see http://msdn.microsoft.com/en-us/library/ms649009(VS.85).aspx. You may also want to look at http://www.flounder.com/wm_copydata.htm.

The dwData member is defined by you. Think of it like a data type enum that you get to define. It is whatever you want to use to identify that the data is a such-and-such string.

The cbData member is the size in bytes of the data pointed to by lpData. In your case, it will be the size of the string in bytes.

The lpData member points to the data you want to copy.

So, to transfer a single string....

LPCTSTR lpszString = ...;
COPYDATASTRUCT cds;
cds.dwData = 1; // can be anything
cds.cbData = sizeof(TCHAR) * (_tcslen(lpszString) + 1);
cds.lpData = lpszString;
SendMessage(hwnd, WM_COPYDATA, (WPARAM)hwnd, (LPARAM)(LPVOID)&cds);

Then, to receive it....

COPYDATASTRUCT* pcds = (COPYDATASTRUCT*)lParam;
if (pcds->dwData == 1)
{
LPCTSTR lpszString = (LPCTSTR)(pcds->lpData);
// do something with lpszString...
}

Is it safe to use WM_COPYDATA between 64-and 32-bit (WOW64) apps?

It's safe only when we follow the rule how to use it. Please refer the remarks of WM_COPYDATA message from below:

The data being passed must not contain pointers or other references to
objects not accessible to the application receiving the data.

While this message is being sent, the referenced data must not be
changed by another thread of the sending process.

The receiving application should consider the data read-only. The
lParam parameter is valid only during the processing of the message.
The receiving application should not free the memory referenced by
lParam. If the receiving application must access the data after
SendMessage returns, it must copy the data into a local buffer.

For example, if we are trying to passing the data type: ULONG_PTR, then the data copy maybe not function well when pass it from 64-bit application to 32-bit application. Because it is 32-bit on 32-bit application and 64-bit on 64-bit application.

You can test it via modify the code below:

struct nmLockInfoType {
char filePathID[1024];
ULONG_PTR point64_32;
// More elements will be added later!
};

The scenario mentioned above, which should be safe as the result you tested. Feel free to let me know if you still have concern about.

In-addition, below is an helpful document about developing 64-bit application for your reference:

Common Visual C++ 64-bit Migration Issues

Is it possible to send a window handle with WM_COPYDATA?

An HWND is not a pointer. You most likely want:

COPYDATASTRUCT cds;
cds.dwData = 20;
cds.cbData = sizeof(HWND);
cds.lpData = &targetWnd;
// ^
LRESULT l = SendMessage(myhWnd, WM_COPYDATA, (WPARAM)nullptr, (LPARAM)&cds);

Also, there seems to be some confusion between the source and destination HWNDs, but perhaps that's just the way you named them.

As Jonathan Potter (and some of the other commenters) point out, there are more efficient ways of sending an HWND, if that's all you want to do.

How can I send data between 2 applications using SendMessage?

As noted in comments, you must use SendMessage with WM_COPYDATA. The primary reason for this is that the message sender is responsible for cleaning up the resources used for the transfer. As noted in the documentation :

The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer.

The only way this can work is if the message sender waits for the receiver to process the message and return a result. Otherwise the sender cannot know when it is safe to release those resources.

PostMessage is asynchronous and returns immediately so this is simply not viable. SendMessage will block until the receiver processes the message and assigns a return value.

Here you are passing a pointer to a stack allocated (local variable) record @DataStruct. Further, you are also passing a pointer to a string which is a local variable. If you use PostMessage, this method will return immediately - the stack locations (for value types like the record) will become invalid and susceptible to being overwritten. The string lives on the heap but is reference counted and, in this case, will be freed when the method returns.

The solution is to always be sure to use SendMessage with WM_COPYDATA.

Sending WM_COPYDATA but getting WM_ACTIVATEAPP on the receiving side

Getting a WM_ACTIVATEAPP message is quite normal, part of the usual notifications that Windows sends. Don't assume that the first message you'll get is WM_COPYDATA, keep looking. If you don't get it at all then the window handle that you used to send the message was wrong. Which is a very common problem, it is not that easy to accurately find a window back.

The .NET framework already has very good support for single-instance apps that can retrieve the command line from a second instance. Consider using it instead. Check this blog post.

C# to C++ process with WM_COPYDATA passing struct with strings

I have it working.

A simple approach is to serialize the struct to a single string and transfer a string.
The swhistlesoft blog was helpful http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

This may be enough to provide the simple messaging.
The struct can be re-constructed at the other end if necessary.

If a struct with any number of strings is to be marshalled as-is then it must be a fixed size, that's the main thing I wasn't getting.
The

MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9)

basically sets the size to match the c++ size which in our case is a TCHAR szTest[ 9 ];

In order to transfer a .Net struct via WM_COPYDATA from c# to c++(/cli) I had to do as follows:

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern bool SetForegroundWindow(IntPtr hWnd);

public static uint WM_COPYDATA = 74;

//from swhistlesoft
public static IntPtr IntPtrAlloc<T>(T param)
{
IntPtr retval = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(param));
System.Runtime.InteropServices.Marshal.StructureToPtr(param, retval, false);
return (retval);
}

//from swhistlesoft
public static void IntPtrFree(IntPtr preAllocated)
{
if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home"));
System.Runtime.InteropServices.Marshal.FreeHGlobal(preAllocated);
preAllocated = IntPtr.Zero;
}

[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public uint dwData;
public int cbData;
public IntPtr lpData;
}

/// <summary>
/// Dot net version of AppInfo structure. Any changes to the structure needs reflecting here.
/// struct must be a fixed size for marshalling to work, hence the SizeConst entries
/// </summary>
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)]
struct AppInfoDotNet
{
public int nVersion;

[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9)]
public string test;
};

To send a string:

    COPYDATASTRUCT cd = new COPYDATASTRUCT();
cd.dwData = 2;

cd.cbData = parameters.Length + 1;
cd.lpData = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(parameters);

IntPtr cdBuffer = IntPtrAlloc(cd);

messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, cdBuffer)) != 0;

To receive string in c++:

else if(pCDS->dwData == 2)
{
//copydata message
CString csMessage = (LPCTSTR)pCDS->lpData;
OutputDebugString("Copydata message received: " + csMessage);
}

To send struct:

            AppInfoDotNet appInfo = new AppInfoDotNet();
appInfo.test = "a test";

COPYDATASTRUCT cds3;
cds3.dwData = 1;
cds3.cbData = System.Runtime.InteropServices.Marshal.SizeOf(appInfo);

IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(appInfo));
System.Runtime.InteropServices.Marshal.StructureToPtr(appInfo, structPtr, false);

cds3.lpData = structPtr;

IntPtr iPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds3));
System.Runtime.InteropServices.Marshal.StructureToPtr(cds3, iPtr, false);

messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, iPtr)) != 0;

System.Runtime.InteropServices.Marshal.FreeCoTaskMem(iPtr);
System.Runtime.InteropServices.Marshal.FreeCoTaskMem(structPtr);

To receive struct in c++:

LRESULT CMainFrame::OnCopyData( WPARAM wParam, LPARAM lParam )
{
LRESULT lResult = FALSE;

COPYDATASTRUCT *pCDS = (COPYDATASTRUCT*)lParam;

//Matching message type for struct
if(pCDS->dwData == 1)
{
AppInfo *pAppInfo = (AppInfo*)pCDS->lpData
lResult = true;
}

Please note this is demo code and needs work in terms of styling, exception handling etc, etc...



Related Topics



Leave a reply



Submit