delphiwm-copydata

delphi passing running parameters to other instance via wm_copydata gives wrong result in Delphi XE2


This code used to work with Delphi 5, but with delphi XE2 does not work as expected. The string passed using wm_copydata will be cut.

procedure SendAppParameters(aMsgStr: string);
var
  hwnd: THandle;
  cds: CopyDataStruct;
begin


  hwnd := FindWindow('TMyAppFormMain', nil); //<-- Substitute window classname with your own
  if hwnd <> 0 then
  begin
   // showmessage(aMsgStr);
    // prepare the data to copy
    cds.dwData := 0;
    cds.cbData := length(AMsgStr);
    cds.lpData := PChar(AMsgStr);
    // activate the destination window
    SetForegroundWindow(hwnd);
    // send the data to the first instance using a wm_CopyData message
    SendMessage(hwnd, wm_CopyData, Application.Handle, integer(@cds));
  end

end;

And in the Main Form I have:

procedure TMyAppFormMain.GotMessage_CopyData(var Msg: TWmCopyData);
var
  MsgString: string;
  I: Integer;
begin

  MsgString := PChar(Msg.CopyDataStruct.lpData);
  showmessage(MsgString);


end;

Solution

  • In fact your code has never been correct. It is broken even on ANSI versions of Delphi.

    Let's take a look. You prepare the message like this:

    cds.cbData := length(AMsgStr);
    cds.lpData := PChar(AMsgStr);
    

    On an ANSI Delphi that means that the text is marshalled up to but not including the null-terminator.

    The receiver does this:

    MsgString := PChar(Msg.CopyDataStruct.lpData);
    

    This relies on there being a null-terminator present. There's no reason to expect that there would be and even to attempt to read beyond cds.cbData bytes is an error.

    The recipient must take care to heed the value of cds.cbData that is sent and not read beyond the end of the buffer.

    Now, the other issue is that you have moved to a Unicode Delphi and so text is now UTF-16 encoded.

    To send the text I would write:

    cds.cbData := length(AMsgStr)*SizeOf(Char);
    cds.lpData := PChar(AMsgStr);
    

    And on the receiving side it should be:

    SetString(MsgString, PChar(Msg.CopyDataStruct.lpData), 
      Msg.CopyDataStruct.cbData div SizeOf(Char));
    

    The cast that you use, integer(@cds), is incorrect. That should be LPARAM(@cds).