如何使用 SendMessage 在两个应用程序之间发送数据?

How can I send data between 2 applications using SendMessage?

我有 2 个应用程序-管理器,代码如下:

procedure TForm1.CopyData(var Msg: TWMCopyData);
var sMsg: String;
begin
  if IsIconic(Application.Handle) then Application.Restore;
  sMsg := PWideChar(Msg.CopyDataStruct.lpData);

  Caption := Caption+'#'+sMsg;

  Msg.Result := 123;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  WM_MY_MESSAGE = WM_USER + 1;
var
  h: HWND;
begin
  Caption := 'X';
  h := FindWindow('TForm1', 'Client');
  if not IsWindow(h) then Exit;

  Caption := Caption+'@';
  SendMessage(h, WM_MY_MESSAGE, 123, 321);
end;

和客户:

procedure TForm1.WndProc(var Message: TMessage);
const
  WM_MY_MESSAGE = WM_USER + 1;
var DataStruct: CopyDataStruct;
    S: String;
    h: HWND;
begin
  inherited;
  if Message.Msg <> WM_MY_MESSAGE then Exit;


  h := FindWindow('TForm1', 'Manager');
  if not IsWindow(h) then Exit;

  Message.Result := 123;

  S := Edit2.Text + '@' + Edit1.Text;
  DataStruct.dwData := 0;
  DataStruct.cbData := 2*Length(S)+1;
  DataStruct.lpData := PWideChar(S);

  Caption := Caption + '#';

  PostMessage(h, WM_CopyData, Form1.handle, integer(@DataStruct));
end;

代码有效 - 但只有一次。 管理器将 2 个整数:123 和 321 作为 "wake up" 消息发送给客户端。 客户端通过发送 Edit1 + Edit2 的内容进行响应。 然后 Manager 获取此数据并显示在其标题上。

为什么它只工作一次?在我再次单击 Button1 后,它什么也没做。

如评论中所述,您必须将 SendMessageWM_COPYDATA 一起使用。这样做的主要原因是消息发送者负责清理用于传输的资源。如前所述 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.

唯一可行的方法是消息发送方等待接收方处理消息并 return 结果。否则发件人无法知道何时可以安全释放这些资源。

PostMessage 是异步的并且 return 是立即的,所以这根本不可行。 SendMessage 将阻塞,直到接收方处理消息并分配 return 值。

这里你传递了一个指向堆栈分配(局部变量)记录的指针@DataStruct。此外,您还传递了一个指向作为局部变量的字符串的指针。如果您使用 PostMessage,此方法将立即 return - 堆栈位置(对于像记录这样的值类型)将变得无效并且容易被覆盖。该字符串位于堆上但被引用计数,在这种情况下,将在 returns.

方法时被释放

解决方案是始终确保将 SendMessageWM_COPYDATA 一起使用。