Reputation: 237
Being new here and to interop and C(++) I did search for an answer on this question but cannot find it on SO or the www. It is my lack in choosing the correct words to search for an answer or I am missing the point of this pretty basic problem...
With interop between Delphi dll and 'C(++) dll caller', do you explicitly state the null terminator character in Delphi dll to pass back to dll caller?
e.g. Dll caller (C-code)
char ErrorMessage[10];
(*GetMessageFromDLLFunction)(&ErrorMessage[0], 10); // (char * ErrorMessage, int buffSize)
Dll code (Delphi):
GetMessageFromDLLFunction(errorMessage: PAnsiChar; buffSize: Integer)
// What if the message generated in this function exceeds 10 characters?
// do I return 9 characters and a null terminator?
tmpMessage := SetLength(tmpMessage, buffSize-1);
tmpMessage := tmpMessage + #0;
System.AnsiStrings.StrCopy(errorMessage,PAnsiChar(tmpMessage));
// or do I return 9 characters without an explicit null terminator?
tmpMessage := SetLength(tmpMessage, buffSize-1);
System.AnsiStrings.StrCopy(errorMessage,PAnsiChar(tmpMessage));
What is best/correct practice?
Upvotes: 3
Views: 885
Reputation: 612794
If you return a string that is sometimes null-terminated, and sometimes not, then you are imposing a heavy complexity burden on the caller. The caller has to, sometimes, add the null-terminator. And the caller faces the awkward situation of having the string in a buffer that is one character too short to contain the null-terminator. It is usually best to push complexity down to as low a level as possible, so my feeling is that, all things being equal, you should always return a null-terminated buffer.
Your code at present makes no attempt to inform the user how large a buffer is needed to store the complete string. You can continue that way but I definitely recommend that you make sure that the returned value is always null-terminated. That would look like this:
function GetString(Buffer: PAnsiChar; Len: Integer): Integer;
var
Value: AnsiString;
begin
if Len < 1 then
begin
Result := STATUS_INVALID_PARAMETER;
exit;
end;
Value := ... // get the string to be returned somehow
StrLCopy(Buffer, PAnsiChar(Value), Len - 1);
if Length(Value) < Len then
Result := STATUS_OK
else
Result := STATUS_BUFFER_TOO_SHORT;
end;
Alternatively you can modify the interface to let the caller know how large a buffer is needed. That might look like this:
function GetString(Buffer: PAnsiChar; var Len: Integer): Integer;
var
Value: AnsiString;
begin
if Len < 0 then
begin
Result := STATUS_INVALID_PARAMETER;
exit;
end;
Value := ... // get the string to be returned somehow
if Len > 0 then
StrLCopy(Buffer, PAnsiChar(Value), Len - 1);
if Length(Value) < Len then
Result := STATUS_OK
else
Result := STATUS_BUFFER_TOO_SHORT;
Len := Length(Value) + 1;
end;
Upvotes: 4
Reputation: 595402
That is entirely up to you to decide. Since you are passing the buffer length as input, you can go either way. But if you choose to omit the null in the output, you should have the function return how many characters were actually written into the buffer so the caller knws where to stop reading. You could even go as far as return an error instead to let the caller know the buffer was too small to receive the full output, if the full message is important.
Upvotes: 3