Sending a String using SendMessage
-
Hi all, I'm looking for an easy way to send message containing String data from C# application to C++ application, using the SendMessage API call. Please refer to both sending the message and receiving it (in C++). Thanks a lot! Eyal.
are both applications exe ?
-
Hi all, I'm looking for an easy way to send message containing String data from C# application to C++ application, using the SendMessage API call. Please refer to both sending the message and receiving it (in C++). Thanks a lot! Eyal.
On the C# side, something like this maybe:
[DllImport("User32", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
...string ManagedString = "A string!";
IntPtr PtrToUnmanagedString = Marshal.StringToHGlobalAnsi(ManagedString);
SendMessage(hwnd, WM_SOMEMESSAGE, IntPtr.Zero, PtrToUnmanagedString);
Marshal.FreeHGlobal(PtrToUnmanagedString);On the C++ side, handle messages in the app's message loop as usual (kind of a big topic for here). Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
are both applications exe ?
-
On the C# side, something like this maybe:
[DllImport("User32", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
...string ManagedString = "A string!";
IntPtr PtrToUnmanagedString = Marshal.StringToHGlobalAnsi(ManagedString);
SendMessage(hwnd, WM_SOMEMESSAGE, IntPtr.Zero, PtrToUnmanagedString);
Marshal.FreeHGlobal(PtrToUnmanagedString);On the C++ side, handle messages in the app's message loop as usual (kind of a big topic for here). Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
On the C++ side I have lparam of type int. How should I refer to it in order to get my string? Thanks!
For the ANSI string sample I showed, something like this:
const char *pANSIstring = (const char *)lParam;
For Unicode, you could change the StringToHGlobalAnsi to StringToHGlobalUni on the C# side, and on the C++ side, something like:
const wchar_t *pUnicodestring = (const wchar_t *)lParam;
Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
For the ANSI string sample I showed, something like this:
const char *pANSIstring = (const char *)lParam;
For Unicode, you could change the StringToHGlobalAnsi to StringToHGlobalUni on the C# side, and on the C++ side, something like:
const wchar_t *pUnicodestring = (const wchar_t *)lParam;
Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
Hi Mark, The two options you suggested doesn't work: At both cases I get 'Bad Ptr' at the C++ side (I get junk and not string). Maybe the reason is that each one of the applications (C# and C++) uses different memory space, so passing pointers from one to another is meaningless. Or am I missing something? Thanks a lot, Eyal.
-
Hi all, I'm looking for an easy way to send message containing String data from C# application to C++ application, using the SendMessage API call. Please refer to both sending the message and receiving it (in C++). Thanks a lot! Eyal.
You must use the WM_COPYDATA msg in the c# proj declare the following
[StructLayout(LayoutKind.Sequential)]
struct CopyDataStruct
{
public IntPtr dwData;
public int dataSize;
public IntPtr data;
}enum WinMessage { WM\_COPYDATA = 0x4A } class NativeAPI { \[DllImport("User32.dll")\] public static extern int SendMessage(IntPtr hWnd, WinMessage msg, IntPtr wParam, IntPtr lParam); \[DllImport("User32.dll", CharSet = CharSet.Auto)\] public static extern IntPtr FindWindow(string className, string windowName); }
and when you press a button for example
private void button1_Click(object sender, EventArgs e)
{
string s = "This is a message from sharp"; // string to send to the native application
// native window handle
IntPtr hCWindow = NativeAPI.FindWindow("RECIEVE_DATA", "Recieve_Data");
// check and handle the not found (null) case here...// GetBytes returns the bytes of the string but not the terminating null char byte\[\] tmpStringData = System.Text.Encoding.Unicode.GetBytes(s); // so we copy to a new byte array and adding 2 zeros (for unicode) byte\[\] stringData = new byte\[tmpStringData.Length + 2\]; Array.Copy(tmpStringData, stringData, tmpStringData.Length); stringData\[stringData.Length - 2\] = stringData\[stringData.Length - 1\] = 0; CopyDataStruct cds = new CopyDataStruct(); cds.dwData = IntPtr.Zero; // no need for this example cds.dataSize = stringData.Length; // pinning the managed array and getting the address GCHandle gch\_data = GCHandle.Alloc(stringData, GCHandleType.Pinned); cds.data = gch\_data.AddrOfPinnedObject(); // pinning the struct also GCHandle gch\_cds = GCHandle.Alloc(cds, GCHandleType.Pinned); // when sending a WM\_COPYDATA msg wParam is the hWnd of the // sender and lParam a pointer to a COPYDATASTRUCT NativeAPI.SendMessage(hCWindow, WinMessage.WM\_COPYDATA, this.Handle, gch\_cds.AddrOfPinnedObject()); // free the handles gch\_data.Free(); gch\_cds.Free(); }
and in the windowproc of the native app
case WM\_COPYDATA: { PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam; MessageBox(0, (LPC
-
You must use the WM_COPYDATA msg in the c# proj declare the following
[StructLayout(LayoutKind.Sequential)]
struct CopyDataStruct
{
public IntPtr dwData;
public int dataSize;
public IntPtr data;
}enum WinMessage { WM\_COPYDATA = 0x4A } class NativeAPI { \[DllImport("User32.dll")\] public static extern int SendMessage(IntPtr hWnd, WinMessage msg, IntPtr wParam, IntPtr lParam); \[DllImport("User32.dll", CharSet = CharSet.Auto)\] public static extern IntPtr FindWindow(string className, string windowName); }
and when you press a button for example
private void button1_Click(object sender, EventArgs e)
{
string s = "This is a message from sharp"; // string to send to the native application
// native window handle
IntPtr hCWindow = NativeAPI.FindWindow("RECIEVE_DATA", "Recieve_Data");
// check and handle the not found (null) case here...// GetBytes returns the bytes of the string but not the terminating null char byte\[\] tmpStringData = System.Text.Encoding.Unicode.GetBytes(s); // so we copy to a new byte array and adding 2 zeros (for unicode) byte\[\] stringData = new byte\[tmpStringData.Length + 2\]; Array.Copy(tmpStringData, stringData, tmpStringData.Length); stringData\[stringData.Length - 2\] = stringData\[stringData.Length - 1\] = 0; CopyDataStruct cds = new CopyDataStruct(); cds.dwData = IntPtr.Zero; // no need for this example cds.dataSize = stringData.Length; // pinning the managed array and getting the address GCHandle gch\_data = GCHandle.Alloc(stringData, GCHandleType.Pinned); cds.data = gch\_data.AddrOfPinnedObject(); // pinning the struct also GCHandle gch\_cds = GCHandle.Alloc(cds, GCHandleType.Pinned); // when sending a WM\_COPYDATA msg wParam is the hWnd of the // sender and lParam a pointer to a COPYDATASTRUCT NativeAPI.SendMessage(hCWindow, WinMessage.WM\_COPYDATA, this.Handle, gch\_cds.AddrOfPinnedObject()); // free the handles gch\_data.Free(); gch\_cds.Free(); }
and in the windowproc of the native app
case WM\_COPYDATA: { PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam; MessageBox(0, (LPC
Hi oobimoo, Thanks for you reply. The C# part works fine, however, at the C++ side I can retrieve only the first character of the string ((LPCTSTR)pcds->lpData equals to "T"). I tried some other castings (string and char*) but I only get the first character. Any ideas? Thanks! Eyal.
-
Hi oobimoo, Thanks for you reply. The C# part works fine, however, at the C++ side I can retrieve only the first character of the string ((LPCTSTR)pcds->lpData equals to "T"). I tried some other castings (string and char*) but I only get the first character. Any ideas? Thanks! Eyal.
-
Hi Mark, The two options you suggested doesn't work: At both cases I get 'Bad Ptr' at the C++ side (I get junk and not string). Maybe the reason is that each one of the applications (C# and C++) uses different memory space, so passing pointers from one to another is meaningless. Or am I missing something? Thanks a lot, Eyal.
eyalbi007 wrote:
Maybe the reason is that each one of the applications (C# and C++) uses different memory space
You're absolutely right! I'm an idiot :) Sorry about that - I totally spaced on the inter-process requirement. Looks like oobimoo showed you a solution. Mark
Mark Salsbery Microsoft MVP - Visual C++ :java:
-
Your encoding at the c++ side is ascii. As i mentioned you must use the System.Text.Encoding.ASCII.GetBytes(s) instead of the unicode that i use in my example.
Dear oobimoo, Indeed, now it works. However, I'm still concerned about something: if I'm getting this correctly, the calls gch_data.Free() and gch_cds.Free() are responsible for freeing the pinned memory. However, what if the receiving side didn't get the information before this release? Isn't it better to (somehow) release this pinned memory at the receiving side? Consider the following chain of events: 1. Sending the message at C# application. 2. Receiving the message at the C++ application, but right before extracting the sent string: 3. Context switch at the C++ application. 4. The C# application releases the pinned memory. 5. Context switch back to the C++ application. The pinned memory is released and we can't extract the string message. Or maybe these can't happen since SendMessage is blocking? Thanks!
-
Dear oobimoo, Indeed, now it works. However, I'm still concerned about something: if I'm getting this correctly, the calls gch_data.Free() and gch_cds.Free() are responsible for freeing the pinned memory. However, what if the receiving side didn't get the information before this release? Isn't it better to (somehow) release this pinned memory at the receiving side? Consider the following chain of events: 1. Sending the message at C# application. 2. Receiving the message at the C++ application, but right before extracting the sent string: 3. Context switch at the C++ application. 4. The C# application releases the pinned memory. 5. Context switch back to the C++ application. The pinned memory is released and we can't extract the string message. Or maybe these can't happen since SendMessage is blocking? Thanks!
Your are welcome From the msdn (WM_COPYDATA) : "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. " From the msdn (SendMessage) : "The SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message. "
modified on Monday, August 25, 2008 1:08 PM
-
You must use the WM_COPYDATA msg in the c# proj declare the following
[StructLayout(LayoutKind.Sequential)]
struct CopyDataStruct
{
public IntPtr dwData;
public int dataSize;
public IntPtr data;
}enum WinMessage { WM\_COPYDATA = 0x4A } class NativeAPI { \[DllImport("User32.dll")\] public static extern int SendMessage(IntPtr hWnd, WinMessage msg, IntPtr wParam, IntPtr lParam); \[DllImport("User32.dll", CharSet = CharSet.Auto)\] public static extern IntPtr FindWindow(string className, string windowName); }
and when you press a button for example
private void button1_Click(object sender, EventArgs e)
{
string s = "This is a message from sharp"; // string to send to the native application
// native window handle
IntPtr hCWindow = NativeAPI.FindWindow("RECIEVE_DATA", "Recieve_Data");
// check and handle the not found (null) case here...// GetBytes returns the bytes of the string but not the terminating null char byte\[\] tmpStringData = System.Text.Encoding.Unicode.GetBytes(s); // so we copy to a new byte array and adding 2 zeros (for unicode) byte\[\] stringData = new byte\[tmpStringData.Length + 2\]; Array.Copy(tmpStringData, stringData, tmpStringData.Length); stringData\[stringData.Length - 2\] = stringData\[stringData.Length - 1\] = 0; CopyDataStruct cds = new CopyDataStruct(); cds.dwData = IntPtr.Zero; // no need for this example cds.dataSize = stringData.Length; // pinning the managed array and getting the address GCHandle gch\_data = GCHandle.Alloc(stringData, GCHandleType.Pinned); cds.data = gch\_data.AddrOfPinnedObject(); // pinning the struct also GCHandle gch\_cds = GCHandle.Alloc(cds, GCHandleType.Pinned); // when sending a WM\_COPYDATA msg wParam is the hWnd of the // sender and lParam a pointer to a COPYDATASTRUCT NativeAPI.SendMessage(hCWindow, WinMessage.WM\_COPYDATA, this.Handle, gch\_cds.AddrOfPinnedObject()); // free the handles gch\_data.Free(); gch\_cds.Free(); }
and in the windowproc of the native app
case WM\_COPYDATA: { PCOPYDATASTRUCT pcds = (PCOPYDATASTRUCT)lParam; MessageBox(0, (LPC
Yeah! Tks a lot!