I’m trying to use the RegisteredWindowMessage API function to send text from one application to another, and I have the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Common
{
public static class RegisteredMsg
{
private const string MyMessage = "9C7EDA65363F4fdaAF32";
private static IntPtr m_targetWindow = new IntPtr(0xFFFF);
private static object m_object = new object();
private static HandleRef m_handleRef;
private static HandleRef m_handleRef;
public static uint RegisteredMessage
{
get { return m_regMsg; }
private set
{
m_regMsg = RegisterWindowMessage(SynchroMessage);
}
}
//-----------------------------------------------------------------
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
//-----------------------------------------------------------------
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
//-----------------------------------------------------------------
static RegisteredMsg()
{
m_handleRef = new HandleRef(m_object, m_targetWindow);
}
//-----------------------------------------------------------------
public static void PostUpdateMsg(string text)
{
IntPtr lpData = Marshal.StringToHGlobalAuto(text);
IntPtr lpLength = new IntPtr(text.Length);
if (!PostMessage(m_handleRef, RegisteredMessage, lpData, lpLength))
{
throw new Exception("Could not post message.");
}
}
//-----------------------------------------------------------------
public static string GetMessageText(Message msg)
{
string text = "";
int length = msg.LParam.ToInt32();
text = Marshal.PtrToStringAuto(msg.WParam, length);
Marshal.FreeHGlobal(msg.WParam);
return text;
}
}
}
Posting the message works find, but when the receiving application calls GetMessageText, the string contains “\0\0\0\0” (which is NOT what the sending application sent).
I’m calling it like this:
RegisteredMsg.PostUpdateMsg("test");
and receiving it like this:
protected override void WndProc(ref Message msg)
{
base.WndProc(ref msg);
if (Convert.ToUInt32(msg.Msg) == RegisteredMsg.RegisteredMessage)
{
string text = RegisteredMsg.GetMessageText(msg);
}
}
EDIT #0
I also tried it this way, and all of the bytes in the received array are ‘\0’:
//-------------------------------------------------------------------------
public static void PostUpdateMsg(string text)
{
byte[] array = StringToByteArray(text);
IntPtr lpData = Marshal.AllocHGlobal(array.Length);
Marshal.Copy(array, 0, lpData, array.Length);
IntPtr lpLength = new IntPtr(text.Length);
if (!PostMessage(m_handleRef, RegisteredMessage, lpData, lpLength))
{
throw new Exception("Could not post message.");
}
}
//-------------------------------------------------------------------------public static string GetMessageText(Message msg)
{
string text = "";
int length = msg.LParam.ToInt32();
byte[] array = new byte[length];
Marshal.Copy(msg.WParam, array, 0, length);
text = RegisteredMsg.ByteArrayToString(array);
return text;
}
EDIT #1
I also called this method from PostUpdateMessage, just to make sure what I was sending was what I thought I was sending:
private static void TestIntPtr(IntPtr ptr, int length)
{
string text = "";
byte[] array = new byte[length];
Marshal.Copy(ptr, array, 0, length);
text = ByteArrayToString(array); // <<------------
}
When the indicated line is executed, the text variablle is indeed = “test”, so I’m doing it right on the sending side. It looks like the memory is getting cleared before it gets to the receiving application.
EDIT #2
I also tried making the IntPtr (pointing to the string I want to send) global to its parent class to make sure it would live long enough to be viable at the other end. No joy there, either.
EDIT #3
I also reverted back to using the StringToHGlobalAuto, and ran the “is it still okay in the sending app” test (see Edit #1 above), and that test proved that the way I was building the IntPtr was fine as well.
This cannot work by design. A pointer is only valid in the process that created it. Every process gets its own chunk of virtual memory. Retrieving the pointed-to memory content requires ReadProcessMemory(). Or you can allocate memory in the target process with VirtualAllocEx() and write to it with WriteProcessMemory(). Windows supports the WM_COPYDATA message to take care of this for you.
This is all rather low-level and painful. There are much better IPC mechanisms available. The ones that work well in .NET are sockets, pipes, WCF.