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、(おそらくサイズに応じて) 型をレジスタに返します。他のコンパイラは、結果を格納する隠しパラメータを追加します。残念ながら、これらを返す方法に同意しない 2 つのコンパイラを扱っているようです。

out代わりにパラメーターを明示的に使用することで、問題を回避できるはずです。

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

それに応じて C++ コードを更新することを忘れないでください。

Rudy Velthuis はこれについて次のように書いています

これにより、ABCVar 構造体がレジスタ EDX:EAX (上位 32 ビットの EDX と下位ビットの EAX) に返されたことがわかりました。これは、このサイズのレコードであっても、Delphi がレコードに対して行うことではありません。Delphi は、このような戻り値の型を追加の var パラメータとして扱い、何も返しません(したがって、関数は実際にはプロシージャです)。

[...]

Delphi が EDX:EAX の組み合わせとして返す唯一の型は Int64 です。

これは、問題を回避する別の方法が

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

Delphi では、C++ で動作が定義されていない状況でも、このような型パニングが可能であることに注意してください。

于 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 に答える