21

別の質問に対するDavidの回答は、DelphiDLL関数がWideStringを返すことを示しています。を使わずにそれが可能だとは思ってもみませんでしたShareMem

私のテストDLL:

function SomeFunction1: Widestring; stdcall;
begin
  Result := 'Hello';
end;

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;

私の発信者プログラム:

function SomeFunction1: WideString; stdcall; external 'Test.dll';
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
  W: WideString;
begin
  ShowMessage(SomeFunction1);
  SomeFunction2(W);
  ShowMessage(W);
end;

それは動作します、そして私は方法がわかりません。私が知っている規則は、Windows APIで使用される規則です(例:Windows:)GetClassNameW

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;

呼び出し元がバッファと最大長を提供することを意味します。Windows DLLは、長さの制限付きでそのバッファに書き込みます。呼び出し元は、メモリの割り当てと割り当て解除を行います。

もう1つのオプションは、DLLがたとえばを使用してメモリをLocalAlloc割り当て、呼び出し元がを呼び出してメモリの割り当てを解除することLocalFreeです。

DLLの例では、メモリの割り当てと割り当て解除はどのように機能しますか?WideString結果が( )であるため、「魔法」は起こりBSTRますか?そして、なぜWindows APIはそのような便利な規則で宣言されないのですか?(そのような規則を使用する既知のWin32 APIはありますか?)


編集:

DLLをC#でテストしました。
呼び出すSomeFunction1とAV(Attempted to read or write protected memory)が発生します。
SomeFunction2正常に動作します。

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string SomeFunction1();

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res);

...

string s;
SomeFunction2(out s);
MessageBox.Show(s); // works ok
MessageBox.Show(SomeFunction1()); // fails with AV!

これがフォローアップです。

4

1 に答える 1

28

AWideStringは a と同じで、BSTR単に Delphi 名です。メモリ割り当ては、共有 COM アロケータによって処理されますCoTaskMemAlloc。すべての関係者が同じアロケーターを使用するため、あるモジュールで安全に割り当て、別のモジュールで割り当てを解除できます。

したがって、使用する必要がない理由Sharememは、Delphi ヒープが使用されていないためです。代わりに、COM ヒープが使用されます。そして、それはプロセス内のすべてのモジュール間で共有されます。

WideString の Delphi 実装を見ると、次の API への呼び出しが表示されます: SysAllocStringLenSysFreeStringおよびSysReAllocStringLen. これらは、システム提供のBSTRAPI 関数です。

参照する Windows API の多くは、COM の発明よりも前のものです。さらに、呼び出し元によって割り当てられる固定長のバッファーを使用すると、パフォーマンス上の利点があります。つまり、ヒープではなくスタックに割り当てることができます。OleAut32.dllまた、Windows の設計者は、すべてのプロセスに COM ヒープへのリンクを強制し、COM ヒープを維持するための代償を払うことを望んでいないことも想像できます。ほとんどの Windows API が設計されたとき、一般的なハードウェアのパフォーマンス特性は現在とは大きく異なっていたことを思い出してください。

BSTRより広く使用されていないもう 1 つの考えられる理由は、Windows API が C を対象としていることです。またBSTR、C からの有効期間の管理は、C++、C#、Delphi などの高レベル言語からの場合よりもはるかにトリッキーです。

ただし、さらに複雑な問題があります。戻り値の Delphi ABIWideStringは、Microsoft ツールと互換性がありません。戻り値の型として使用しないでください。WideString代わりに、outパラメーターを介して返します。詳細については、WideString を相互運用の関数の戻り値として使用できないのはなぜですか? を参照してください。

于 2012-02-17T15:35:09.630 に答える