7

我现在正在经历一些非常奇怪的事情。当我将结构从 C++ 传递到 Delphi DLL 作为参数时,一切正常。但是,一旦我想收到一条记录,我就会得到错误的值或异常。我停用了记录的对齐方式,以便通过它们应该可以工作!这是代码!

德尔福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++ 调用:

#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();
        }
    }

我不知道为什么将这条记录作为参数传递而不是作为函数的结果!?

有人可以帮助我吗?

谢谢

PS:正如我所说,C++ 和 Delphi 都显示记录是 8 字节大。

4

2 回答 2

5

一些编译器会struct在寄存器中返回类型(可能取决于大小),其他编译器会添加一个隐藏的额外参数,结果应该存储在其中。不幸的是,看起来您正在处理两个不同意如何返回它们的编译器。

您应该能够通过显式使用out参数来避免该问题。

procedure TTest(out Result: TSimpleRecord); cdecl;
begin
  Result.Nr1 := 1;
  Result.Nr2 := 201;
end;

不要忘记相应地更新 C++ 代码。

Rudy Velthuis 曾写过这样的文章

这表明 ABCVar 结构在寄存器 EDX:EAX 中返回(EDX 具有前 32 位,EAX 具有较低的位)。这根本不是 Delphi 对记录所做的,即使是这种大小的记录也是如此。Delphi 将此类返回类型视为额外的 var 参数,并且不返回任何内容(因此该函数实际上是一个过程)。

[...]

Delphi 作为 EDX:EAX 组合返回的唯一类型是 Int64。

这表明避免该问题的另一种方法是

function TTest() : Int64; cdecl;
begin
  TSimpleRecord(Result).Nr1 := 1;
  TSimpleRecord(Result).Nr2 := 201;
end;

请注意,即使在 C++ 中未定义行为的情况下,Delphi 也允许这种类型双关语。

于 2013-04-20T10:17:26.350 回答
1

Delphi 不遵循返回值的平台标准 ABI。标准 ABI 按值将返回值传递给调用者。Delphi 将返回值视为隐式的额外 var 参数,在所有其他参数之后传递。文档描述了规则。

您可以更改您的调用代码以匹配它。在 C++ 函数中传递对 struct 参数的额外引用。

typedef void (__cdecl TestFunc)(TSimpleRecord&);     

如果您要在 C++ 端执行此操作,则最好在 Delphi 端进行相同的更改以清楚起见。

由于 Delphi 不遵循返回值的平台标准,我建议您将自己限制为与其他工具兼容的类型。这意味着高达 32 位的整数值、指针和浮点值。

作为一般经验法则,不要打包唱片。如果这样做,您将出现影响性能的错位。对于问题中的记录,无论如何都不会有填充,因为两个字段的大小相同。

于 2013-04-20T11:02:15.657 回答