Delphi DLL が ShareMem を使用せずに WideString を使用できるのはなぜですか?
-
27-10-2019 - |
質問
別の質問に対するデイビッドの答え は、 WideString を返す Delphi DLL 関数を示しています。を使わずにそれが可能だとは思いませんでした 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 は、長さ制限付きでそのバッファに書き込みます。呼び出し元はメモリの割り当てと割り当て解除を行います。
別のオプションは、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!
がここにあります フォローアップ.
解決
あ WideString
と同じです BSTR
, 、それは単なる Delphi の名前です。メモリ割り当ては共有 COM アロケータによって処理されます。 CoTaskMemAlloc
. 。すべての関係者が同じアロケータを使用するため、あるモジュールで安全に割り当て、別のモジュールで割り当てを解除できます。
したがって、使用する必要がない理由は、 Sharemem
Delphi ヒープが使用されていないということです。代わりに COM ヒープが使用されます。そして、それはプロセス内のすべてのモジュール間で共有されます。
Delphi の WideString 実装を見ると、次の API への呼び出しがあることがわかります。 SysAllocStringLen
, SysFreeString
そして SysReAllocStringLen
. 。これらが提供されるシステムです BSTR
API関数.
あなたが参照している Windows API の多くは、COM が発明される以前のものです。さらに、呼び出し元によって割り当てられた固定長のバッファーを使用すると、パフォーマンス上の利点があります。つまり、ヒープではなくスタックに割り当てることができます。また、Windows の設計者が、すべてのプロセスに強制的にリンクすることを望んでいないことも想像できます。 OleAut32.dll
そして、COM ヒープを維持するための代償を支払います。Windows API のほとんどが設計された当時、一般的なハードウェアのパフォーマンス特性は現在とは大きく異なっていたことを思い出してください。
使用しないもう 1 つの理由として考えられるのは、 BSTR
もっと広く言えば、Windows API は C を対象としているということです。そして、その寿命を管理するのは、 BSTR
C からの実行は、C++、C#、Delphi などの高水準言語からの実行よりもはるかに注意が必要です。
ただし、さらに複雑な問題があります。Delphi ABI の目的 WideString
戻り値は Microsoft ツールと互換性がありません。使用しないでください WideString
戻り値の型として使用する代わりに、 out
パラメータ。詳細については、を参照してください。 WideString を相互運用機能の関数の戻り値として使用できないのはなぜですか?