Использование User32.dll SendMessage для отправки строки в другое приложение Windows Forms

У меня странная проблема.

Я использую SendMessage для отправки строки всем запущенным экземплярам одного и того же приложения Windows Forms.

Я могу успешно отправить строковое представление числового значения указателя IntPtr, например:

 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);
    }

Приложение-получатель правильно выводит значение типа «123553».

Однако, если я вручную присвою значение s, ничего не будет получено:

 string s = "123553";

Есть ли у кого-нибудь идеи, почему вызов ToString для IntPtr и жесткое кодирование значения не приводят к одинаковому поведению?

Код для самостоятельного запуска приложения находится здесь:

    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));
        }
    }

Источники:

Обнаружение, если другой экземпляр приложения уже запущен

Использование WM_COPYDATA для межпроцессного взаимодействия (VFP9)


person hlintrup    schedule 12.01.2013    source источник
comment
Вы назначаете эту строку в том же самом месте? Лучше введите вариант как комментарий, чтобы не было путаницы. Это кажется очень странным.   -  person Henk Holterman    schedule 12.01.2013
comment
Строковые константы в C # интернированы для экономии памяти. Может быть, это влияет на StringToHGlobalHUni (не должно). Чтобы исключить это, добавьте строку s = String.Intern(s); сразу после вашего handle.ToString() звонка.   -  person Anders Abel    schedule 12.01.2013
comment
Я запускаю два экземпляра приложения, где s назначен handle.ToString (). Одно приложение правильно получает сообщение от другого. Затем я присваиваю s жестко запрограммированное значение (конвертируемое в Int32), и оно терпит неудачу; сообщения не получены.   -  person hlintrup    schedule 12.01.2013
comment
Очень мало смысла преобразовывать IntPtr в строку, а затем из строки в int. Кроме того, 1-й аргумент SendMessage () - это IntPtr, а не int. Более того, посылать окну собственное значение дескриптора загадочно, оно уже известно. Это просто терпит неудачу при изменении значений Handle, они не имеют того же значения, когда вы перезапускаете программу или делаете что-либо, что вызывает изменение свойства Handle формы.   -  person Hans Passant    schedule 12.01.2013
comment
Приложение не отправляет свой дескриптор самому себе; другие запущенные экземпляры получают его. Это POC, чтобы увидеть, какие запущенные экземпляры что получили друг от друга, и когда я попытался отправить любое другое значение, кроме дескриптора окна, я увидел, что это не удалось, отсюда и этот пост. COPYDATASTRUCT ожидает IntPtr, Marshal.StringToHGlobalUni ожидает строку, а параметр hWnd SendMessage ожидает int, следовательно, все преобразования. Хотя я мог бы использовать IntPtr.ToInt32(). @AndersAbel String.Intern не повлиял на StringToGlobalHUni. Спасибо вам за ваши предложения.   -  person hlintrup    schedule 13.01.2013
comment
Я поискал еще немного и нашел эту ссылку, и она работает. Подпись SendMessage иная COPYDATASTRUCT. Я недостаточно разбираюсь в этом, чтобы сказать, почему это работает, но это действительно так. Спасибо, что нашли время оставить отзыв.   -  person hlintrup    schedule 13.01.2013