A weird thing happens when sending a string from a Delphi 10.4 app to a Delphi 6 app: only the first character is caught on the receiver.
I already found some other topics on the web about this issue, but I couldn't solve it.
Here's the code:
Sender - Delphi 10.4
procedure TfrmMain.sendMessageToApp(psValue: string);
var
DataStruct: CopyDataStruct;
begin
DataStruct.dwData := 0;
DataStruct.cbData := SizeOf(PChar(psValue));
DataStruct.lpData := PChar(psValue);
SendMessage(
FindWindow(nil, PChar(Self.psFormTitleERP)),
WM_COPYDATA, Self.Handle, Integer(@DataStruct));
end;
Receiver - Delphi 6
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
psMessage: String;
begin
psMessage := PChar(Msg.CopyDataStruct.lpData);
ShowMessage(psMensagem);
end;
One interesting thing I saw is a difference on tagCOPYDATASTRUCT between the two versions of Delphi: the dwData parameter is DWORD for Delphi 6, and ULONG_PTR for Delphi 10.4.
So... I have no idea what's wrong with these routines. Can anyone help me?
I think it could be some data conversion error, but I'm not so sure - I'm not used to manipulating bytes this deep.
Wrong size
First you should read the documentation for the
WM_COPYDATAmessage and theCOPYDATASTRUCTstructure. According to the latter, thecbDatamember should be the number of bytes in the data pointed to by thelpDatamember.You send
SizeOf(PChar(psValue))which isSizeOf(Pointer)which is 4 or 8, depending on whether your app is 32-bit or 64-bit.But you should instead send the length of the data, which is
(Length(psValue) + 1) * SizeOf(Char).Before Delphi 2009, strings were ANSI (1 byte per char). In Delphi 2009 and later, strings are Unicode (2 bytes per char). So the factor to the right is either
1or2.The
+ 1is because you probably also want to send the NULL character at the end of the string (assuming the string you want to send is not empty).64-bit pointers
Further, in a 64-bit app (which your 10.4 app may or may not be), a pointer is 64 bit wide, so it won't fit in an
Integer(still 32 bit). So in a 64-bit app,Integer(@DataStruct)is clearly wrong; you discard the upper 32 bits. It's like your phone number is 1234 5678 but you tell me that it is 0000 5678; I won't be able to call you.You want
LPARAM(@DataStruct). (NativeIntand a few others will also work; the important thing is that you get right pointer size.)String types
Again, recall that strings became Unicode (2 bytes per char) in Delphi 2009.
So you send a Unicode string, but the receiver expects an ANSI string.
The simplest solution is to send an ANSI string. To do this, use
AnsiStringandPAnsiCharinstead ofstringandPChar.Finding the right window
You really should verify that you actually have found the right window. If you don't find it, you likely want to handle that scenario in some appropriate way.
I'd personally match on both class name and window name, to make it a bit safer. Of course, there may be other windows with the same name on the end user's desktop. You may also want to consider the case when there are several (more than one) matching windows.
At the receiver
At the receiver,
PChar(Msg.CopyDataStruct.lpData)will work fine since we do have the terminating NULL character in thelpDatabuffer (or the pointer isnil).It is also good practice to look at the
cbDataparameter to make sure that you don't read beyond the end of the buffer.One possible solution is to use the
SetStringfunction:But if so, please first check that
lpDataisn'tnil(why?).(If you base the receiver's behaviour on
cbData, you can also omit sending the NULL terminator in the first place. Clearly, there are many ways to do this, each with its own security implications. Really, we could write a book on this topic!)