WM_COPYDATA from VB.Net to Delphi 10.4

113 Views Asked by At

I'm trying to move an application from Lazarus to Delphi 10.4. The problem I am having is with WM_COPYDATA.

On the VB.Net application:

        Dim sarr As Byte() = System.Text.Encoding.[Default].GetBytes(msg)
        Dim len As Integer = sarr.Length
        Dim cds As COPYDATASTRUCT
        cds.dwData = CType(wParam, IntPtr)
        cds.lpData = msg
        cds.cbData = len + 1
        SendMessage(CInt(hWnd), WM_COPYDATA, 0, cds)

On the Delphi side:

    WM_COPYDATA :
       begin
            recvdata := PCopyDataStruct(Message.LParam);
            msgtype := recvdata^.dwData;
            msglen := recvdata^.cbData;
            str := StrPas(PCHAR(Recvdata^.lpData));

The msgtype and msglen are coming across fine, but the str (lpdata) is corrupted. I think this has something to do with Delphi using widechar or ANSI.

What do I need to do on the VB.Net and Delphi 10.4 sides to get them to line up ?

Thanks !

1

There are 1 best solutions below

0
On

In Delphi 2009 and later, PChar is an alias for PWideChar (16bit UTF-16), but the VB.NET code is sending 8bit ANSI encoded bytes instead. So, you need to use PAnsiChar instead of PChar on the Delphi side (unless you change the VB.NET code to use Encoding.Unicode instead of Encoding.Default).

Which BTW, there is a (potential) bug in the VB.NET code. DotNet strings are not null-terminated, so Encoding.GetBytes(msg) will not output a null terminator byte if msg is a String (it will if msg is a Char[] array with a null terminator present). The code is setting cds.cbData = len + 1 as if a null terminator were present. It should instead set cds.cbData = len. And then, on the Delphi side, you can use SetString() instead of StrPas() to get the PAnsiChar data into an AnsiString, which you can then assigned to a native string.

Try this:

Dim sarr As Byte() = System.Text.Encoding.[Default].GetBytes(msg)
Dim len As Integer = sarr.Length
Dim cds As COPYDATASTRUCT
cds.dwData = CType(wParam, IntPtr)
cds.lpData = msg
cds.cbData = len
SendMessage(CInt(hWnd), WM_COPYDATA, 0, cds)
var
  ...
  astr: AnsiString;
  str: String;
...
  WM_COPYDATA :
  begin
    recvdata := PCopyDataStruct(Message.LParam);
    msgtype := recvdata^.dwData;
    msglen := recvdata^.cbData;
    SetString(astr, PAnsiChar(Recvdata^.lpData), msglen);
    str := string(astr);
    ...
  end;

Once that is working, consider using Encoding.UTF8 instead of Encoding.Default to avoid any potential data loss of non-ASCII characters, since Encoding.Default is a locale-specific and potentially lossy conversion, whereas UTF-8 is a loss-less conversion:

Dim sarr As Byte() = System.Text.Encoding.UTF8.GetBytes(msg)
Dim len As Integer = sarr.Length
Dim cds As COPYDATASTRUCT
cds.dwData = CType(wParam, IntPtr)
cds.lpData = msg
cds.cbData = len
SendMessage(CInt(hWnd), WM_COPYDATA, 0, cds)
var
  ...
  utf8: UTF8String;
  str: String;
...
  WM_COPYDATA :
  begin
    recvdata := PCopyDataStruct(Message.LParam);
    msgtype := recvdata^.dwData;
    msglen := recvdata^.cbData;
    SetString(utf8, PAnsiChar(Recvdata^.lpData), msglen);
    str := string(utf8);
    ...
  end;