2

I have a strange problem.

I am using SendMessage to send a string to all running instances of the same Windows Forms application.

I can successfully send the string representation of the numeric value of an IntPtr pointer, like so:

 unsafe private void SendString(IntPtr handle, IntPtr myHandle)
    {
        string s = handle.ToString(); // This will work and the value will be received.
                                      // Try with "123553" which wont work.
                                      // How can that be?
        IntPtr lpData = Marshal.StringToHGlobalUni(s);

        COPYDATASTRUCT data = new COPYDATASTRUCT();
        data.dwData = 0;
        data.cbData = s.Length * 2;
        data.lpData = lpData;

        IntPtr lpStruct = Marshal.AllocHGlobal(
            Marshal.SizeOf(data));

        Marshal.StructureToPtr(data, lpStruct, false);

        int hTarget;
        var succes = Int32.TryParse(s, out hTarget);

        if (succes)
            SendMessage(hTarget, WM_COPYDATA, handle, lpStruct);
    }

The receiving application(s) correctly outputs a value like '123553'.

However, if I manually assign a value to s nothing is received:

 string s = "123553";

Does anyone have an idea why calling ToString on an IntPtr and hardcoding the value doesn't produce the same behavior?

The code for running the application yourself is here:

    public const int WM_COPYDATA = 0x004a;

    [StructLayout(LayoutKind.Sequential)]
    public struct COPYDATASTRUCT
    {
        [MarshalAs(UnmanagedType.I4)]
        public int dwData;
        [MarshalAs(UnmanagedType.I4)]
        public int cbData;
        [MarshalAs(UnmanagedType.SysInt)]
        public IntPtr lpData;
    }

    [DllImport("User32.dll")]
    private static extern bool SendMessage(int hWnd,
        int wMsg, IntPtr wParam, IntPtr lParam);

    public Form1()
    {
        InitializeComponent();
    }

    unsafe protected override void WndProc(ref Message message)
    {
        if (message.Msg == WM_COPYDATA)
        {
            COPYDATASTRUCT data = (COPYDATASTRUCT)
                message.GetLParam(typeof(COPYDATASTRUCT));

            string str = new string((char*)(data.lpData),
                0, data.cbData / 2);

            Debug.WriteLine(str);
        }
        base.WndProc(ref message);
    }

    unsafe private void SendString(IntPtr handle, IntPtr myHandle)
    {
        string s = handle.ToString();
        IntPtr lpData = Marshal.StringToHGlobalUni(s);

        COPYDATASTRUCT data = new COPYDATASTRUCT();
        data.dwData = 0;
        data.cbData = s.Length * 2;
        data.lpData = lpData;

        IntPtr lpStruct = Marshal.AllocHGlobal(
            Marshal.SizeOf(data));

        Marshal.StructureToPtr(data, lpStruct, false);

        int hTarget;
        var succes = Int32.TryParse(s, out hTarget);

        if (succes)
            SendMessage(hTarget, WM_COPYDATA, handle, lpStruct);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Process currentProcess = Process.GetCurrentProcess();

        var handles = (from process in Process.GetProcesses()
                       where
                         process.Id != currentProcess.Id &&
                         process.ProcessName.Equals(
                           currentProcess.ProcessName,
                           StringComparison.Ordinal)
                       select process.MainWindowHandle).ToList<IntPtr>();

        foreach (var handle in handles)
        {
            SendString(handle, this.Handle);
            Debug.WriteLine(string.Format("Sending handle {0} from handle {1}", handle, this.Handle));
        }
    }

Sources:

Detecting if another instance of the application is already running

Using WM_COPYDATA for interprocess communication (VFP9)

Community
  • 1
  • 1
hlintrup
  • 169
  • 1
  • 13
  • You assign that string at that exact same spot? Better put the variant in as a comment, to prevent confusion. It seems very strange. – H H Jan 12 '13 at 14:58
  • String constants in C# are *interned* to save memory. Maybe it affects `StringToHGlobalHUni` (it shouldn't). To rule out, add a line `s = String.Intern(s);` right after your `handle.ToString()` call. – Anders Abel Jan 12 '13 at 15:34
  • I run two instances of the application where s is assigned handle.ToString(). One application correctly receives a message from the other. I then assign s the hardcoded value (convertible to Int32) and it fails; no messages are received. – hlintrup Jan 12 '13 at 15:37
  • 1
    It makes very little sense to convert an IntPtr to a string and then from a string to an int. Furthermore, the 1st argument of SendMessage() is IntPtr, not int. Furthermore, sending a window its own handle value is mysterious, it already knows. This just fails outright when Handle values change, they do not have the same value when you restart the program or do otherwise anything that causes the form's Handle property to change. – Hans Passant Jan 12 '13 at 15:41
  • The app doesn't send its handle to itself; other running instances receive it. This is POC to see which running instances received what from each other and when I tried to send any other value besides a window handle I saw it fail, hence this post. `COPYDATASTRUCT` expects an IntPtr, `Marshal.StringToHGlobalUni` expects a string and the `hWnd` parameter of SendMessage expects an int, hence all the conversions. I could use `IntPtr.ToInt32()`, though. @AndersAbel String.Intern didn't affect `StringToGlobalHUni`. Thank you for your suggestions. – hlintrup Jan 13 '13 at 11:43
  • I've searched some more and found this [link](http://stackoverflow.com/questions/2066658/postmessage-unable-to-pass-a-string-c-sharp) and it works. The signature of `SendMessage` is different and `COPYDATASTRUCT` is different. I am not enough in to this to tell why it works, but it does. Thank you for taking the time to give feedback. – hlintrup Jan 13 '13 at 11:57

0 Answers0