2

I'm trying to make to Delphi applications communicate with each other via WM_COPYDATA. The problem I'm having though is is that the sender app is written in Delphi 7 and the receiver is written in Delphi 10.2 . I copied my Delphi 7 program's code into Delphi 10 and the communication worked perfectly. Using the exact same code in Delphi 7 however caused my string being passed to the receiver app to get corrupted. The codes I use are as follows:

One the sending side I have:

procedure TSenderApp.SendString(ToSend: string);
var
 copyDataStruct : TCopyDataStruct;

  receiverHandle : THandle;
   res : integer;
begin
 copyDataStruct.dwData := 140500; //use it to identify the message contents
 copyDataStruct.cbData := (1+ Length(ToSend))* SizeOf(Char) ;
 copyDataStruct.lpData := pchar(ToSend) ;

 receiverHandle := FindWindow(PChar('TRecieverApp'),PChar('RecieverApp')) ;
   if receiverHandle = 0 then
   begin
     ShowMessage('CopyData Receiver NOT found!') ;
     Exit;
   end;

   res := SendMessage(receiverHandle, WM_COPYDATA, Integer(Handle), 
   LPARAM(@copyDataStruct)) ;
end;

And on the receiving side I have:

procedure TRecieverApp.WMCopyData(var Message: TMessage);
var
  p : PCopyDataStruct;
  l : Integer;
  s : string;
begin
  p := PCopyDataStruct( Message.lParam );

  if (p <> nil) then
  begin
  ShowMessage('New Message Recieved!');
    l := p^.cbData;
    SetLength( s, (l+1) );
    StrLCopy( PChar(s), PChar(p^.lpData), l );
    Edit1.Text := s;
  end
  else
    Edit1.Text := 'ERROR';
end;

What am I doing wrong? Or why is the message string being corrupted when sent from the Delphi 7 written SenderApp and not from the Delphi 10 written SenderApp?

2 Answers2

7

You are sending and processing data using the native encoding of Char, which is AnsiChar in Delphi 7 but is WideChar in Delphi 10.2 Tokyo (Delphi switched everything to Unicode in D2009). When Delphi 7 sends the data as ANSI, Delphi 10.2 misinterprets it as UTF-16. And vice versa. So you end up with corruption either way.

You need to convert the data to an agreed-upon character encoding before sending it, and convert it from that encoding after receiving it.

Try something more like this:

{$IF CompilerVersion >= 24} // XE3+
{$LEGACYIFEND ON} 
{$IFEND}

var
  MyDataID: UINT = 0;

procedure TSenderApp.SendString(const ToSend: string);
var
  copyDataStruct : TCopyDataStruct;
  receiverHandle : HWND;
  res : LRESULT;
  s : UTF8String;
begin
  if MyDataID = 0 then
  begin
    ShowMessage('CopyData ID NOT registered!');
    Exit;
  end;

  receiverHandle := FindWindow('TRecieverApp', 'RecieverApp');
  if receiverHandle = 0 then
  begin
    ShowMessage('CopyData Receiver NOT found!');
    Exit;
  end;

  {$IF CompilerVersion >= 20} // D2009+
  s := UTF8String(ToSend);
  {$ELSE}
  s := UTF8Encode(ToSend);
  {$IFEND}

  copyDataStruct.dwData := MyDataID; //use it to identify the message contents
  copyDataStruct.cbData := Length(s) * SizeOf(AnsiChar);
  copyDataStruct.lpData := PAnsiChar(s);
  res := SendMessage(receiverHandle, WM_COPYDATA, WPARAM(Handle), LPARAM(@copyDataStruct));
end;

initialization
  MyDataID := RegisterWindowMessage('MyDataID');

{$IF CompilerVersion >= 24} // XE3+
{$LEGACYIFEND ON} 
{$IFEND}

var
  MyDataID: UINT = 0;

procedure TRecieverApp.WMCopyData(var Message: TMessage);
var
  p : PCopyDataStruct;
  s : UTF8String;
begin
  p := PCopyDataStruct(Message.lParam);
  if (p <> nil) and (MyDataID <> 0) and (p^.dwData = MyDataID) then
  begin
    SetString(s, PAnsiChar(p^.lpData), p^.cbData);
    {$IF CompilerVersion >= 20} // D2009+
    Edit1.Text := String(s);
    {$ELSE}
    Edit1.Text := UTF8Decode(s);
    {$IFEND} 
    ShowMessage('New Message Received!');
  end else
    inherited;
end;

initialization
  MyDataID := RegisterWindowMessage('MyDataID');
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

The difference between the two Delphi versions is string format. In Delphi 2007 and earlier, string uses 1-byte AnsiChar characters in ANSI format. In Delphi 2009 and later, string uses 2-byte WideChar characters in UTF-16 format. You need to convert the data to a common character encoding when sending it.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
M_W
  • 76
  • 6
  • 1
    It would be best to send the data using `WideChar` with UTF-16, or `AnsiChar` using UTF-8. Don't use ANSI at all. – Remy Lebeau Nov 25 '17 at 17:17
  • @RemyLebeau Thank you, figured that out in the meantime, but have no idea how to convert the data. – Adriaan Roets Nov 25 '17 at 17:27
  • You need to dereference the pointer you are passing in the first parameter of `Move()`, and remove the `@` in the second parameter: `Move(PAnsiChar(p^.lpData)^, S[1], l)`. Also, your `cbData` value already includes the null terminator, so you dont need to use `+1` on the receiver side. If anything, you should use `-1` to ignore the null terminator. Or better, just don't send the null terminator at all, you don't need it – Remy Lebeau Nov 25 '17 at 17:56
  • @RemyLebeau Got it working, thank you for the input. – Adriaan Roets Nov 25 '17 at 17:59