I've got some old C DLLs that serve as intermediaries between my Delphi app and MATLAB compiled DLLs. These were originally developed with Delphi 7, and worked fine. Now, however, I've upgraded first to XE7 and now to 10.2.3, and I'm having problems. Basically, I have to pass a pointer to a record to the DLL; the structure includes fields that are pointers to old-fashioned C-style strings (i.e. an array of chars, where each char is a byte, and terminated by a 0 byte).
So consider this record:
MyRec = record
id: Integer;
name: PByte;
end;
I wrote a function like this:
function MakeCString(S: String): TBytes;
var
I: Integer;
len: Integer;
begin
len := Length(S);
SetLength(Result, len + 1);
if len > 0 then
begin
for I := 0 to (len - 1) do
begin
Result[i] := Byte(S[I + 1]);
end;
end;
Result[len] := 0;
end;
Then, my intent is to use this like this:
var r: MyRec;
r.id := 27;
r.name := MakeCString('Jimbo');
callMatlabWrapperDll(@r);
I know this is ugly and prone to memory loss - if it even works reliably at all! It seems to sort of work, but now on a different Windows 10 machine from where it worked (maybe), on my dev machine (also Windows 10), I get errors.
I've banged my head on this long enough. What I need is a way to create a C-style string, pass its address to a DLL, and clean up afterwards. (Note: This is all Win32, running on Win64).
Thanks.
Your MakeCString()
is implemented wrong. It is not converting Unicode characters to ANSI, it is just truncating the characters as-is from 16-bit to 8-bit, which is not the same thing.
For that matter, you don't actually need MakeCString()
at all. You can use TEncoding.GetBytes()
instead.
Also, you are assigning the result of your "conversion" directly to your record's PByte
field. You should first assign it to a local TBytes
variable, and then get a pointer to its data.
Try this instead:
type
MyRec = record
id: Integer;
name: PByte;
end;
procedure callMatlab;
var
r: MyRec;
name: TBytes;
begin
name := TEncoding.Default.GetBytes('Jimbo'#0);
r.id := 27;
r.name := PByte(name);
callMatlabWrapperDll(@r);
end;
Alternatively, you can (and should) use PAnsiChar
instead, which is what C-style APIs generally use for 8-bit strings, eg:
type
MyRec = record
id: Integer;
name: PAnsiChar;
end;
procedure callMatlab;
var
r: MyRec;
name: AnsiString;
begin
name := AnsiString('Jimbo');
r.id := 27;
r.name := PAnsiChar(name);
callMatlabWrapperDll(@r);
end;
This would be more inline with your Delphi 7 code, when String
was still AnsiString
.