I am experiencing some very strange things right now. When I am passing a struct from C++ to a Delphi DLL as a parameter everything works fine. However, as soon as I want to receive a record as a result I either get wrong values or exceptions. I deactivated the alignment of the record so that passing them should work! Heres the code!
Delphi DLL:
TSimpleRecord = packed record
Nr1 : Integer;
Nr2 : Integer;
end;
//...
function TTest() : TSimpleRecord; cdecl;
begin
Result.Nr1 := 1;
Result.Nr2 := 201;
ShowMessage(IntToStr(SizeOf(Result)));
end;
C++ call :
#pragma pack(1)
struct TSimpleRecord
{
int Nr1;
int Nr2;
};
//...
typedef TSimpleRecord (__cdecl TestFunc)(void);
TestFunc* Function;
HINSTANCE hInstLibrary = LoadLibrary("Reactions.dll");
if (hInstLibrary)
{
Function = (TestFunc*)GetProcAddress(hInstLibrary, "TTest");
if (Function)
{
TSimpleRecord Result = {0};
Result = Function();
printf("%d - %d - %d", sizeof(Result), Result.Nr1, Result.Nr2);
cin.get();
}
}
I have got no idea why passing this record as a parameter works but not as a result of a function!?
Can anybody help me?`
Thanks
PS: As I said, both C++ and Delphi show that the record is 8 bytes large.
Delphi does not follow the platform standard ABI for return values. The standard ABI passes return values to the caller by value. Delphi treats the return value as an implicit extra var parameter, passed after all other parameters. The documentation describes the rules.
You can change your calling code to match that. Pass an extra reference to struct parameter in your C++ function.
If you are going to do this on the C++ side, you would be best doing the same change on the Delphi side for clarity.
Since Delphi does not follow the platform standards for return values I suggest you restrict yourself to types that are compatible with other tools. That means integral values up to 32 bits, pointers and floating point values.
As a general rule of thumb, do not pack records. If you do so you will have mis-alignment which affects performance. For the record in the question, there will be no padding anyway since both fields are the same size.
Some compilers will return
struct
types (possibly depending on the size) in registers, others will add a hidden extra parameter where the result should be stored. Unfortunately, it looks like you're dealing two compilers that do not agree on how to return these.You should be able to avoid the problem by explicitly using an
out
parameter instead.Do not forget to update the C++ code accordingly.
Rudy Velthuis has written about this:
which suggests that an alternative way to avoid the problem is
Note that Delphi allows such type punning even in situations where the behaviour would be undefined in C++.