Frage

Davids Antwort auf eine andere Frage zeigt eine Delphi-DLL-Funktion ein Wide zurück. Ich hätte nie gedacht, dass das möglich ohne den Einsatz von ShareMem.

Mein Test DLL:

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

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

Meine Anrufer Programm:

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;

Es funktioniert , und ich verstehe nicht, wie. Die Konvention ich kenne ist die von dem Windows-API verwendet, zum Beispiel Windows-GetClassNameW:

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

den Anrufer Bedeutung liefert den Puffer und die maximale Länge. Das Windows-DLL schreibt in diesen Puffer mit der Längenbegrenzung. Der Anrufer ist reserviert und freigibt den Speicher.

Eine andere Möglichkeit ist, dass die DLL den Speicher zum Beispiel zuteilen durch LocalAlloc verwenden und die Anrufer freigibt den Speicher durch LocalFree aufrufen.

Wie funktioniert die Speicherzuweisung und Aufhebung der Zuordnung mit meinem DLL Beispiel? Ist die „Magie“ geschehen, weil das Ergebnis ist WideString (BSTR)? Und warum nicht Windows-APIs erklärt mit einem solchen bequemen Konvention? (Gibt es bekannte Win32-APIs, die eine solche Konvention verwendet?)


EDIT:

ich die DLL mit C # getestet.
Der Aufruf SomeFunction1 verursacht einen AV (Attempted to read or write protected memory).
SomeFunction2 funktioniert.

[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!

Hier ist ein Followup .

War es hilfreich?

Lösung

Ein WideString ist das gleiche wie ein BSTR, es ist nur der Delphi Namen für sie. Die Speicherzuordnung wird durch die freigegebene COM allocator, CoTaskMemAlloc behandelt. Da alle Parteien das gleiche allocator verwenden, können Sie sicher in einer Modul zuordnen und in einer anderen freigeben.

Also, der Grund, warum Sie brauchen nicht zu verwenden Sharemem ist, dass der Delphi-Heap nicht verwendet wird. Stattdessen wird das COM-Heap verwendet. Und das zwischen allen Modulen in einem Prozess gemeinsam genutzt wird.

Wenn Sie an der Delphi Implementierung von Wide schauen Sie Anrufe auf die folgenden APIs finden Sie unter: SysAllocStringLen , SysFreeString und SysReAllocStringLen . Es handelt sich um das System zur Verfügung gestellt BSTR API-Funktionen .

Viele der Windows-APIs beziehen sich auf Sie schon vor der Erfindung von COM. Was mehr ist, gibt es Leistungsvorteile einer festen Länge Puffer zu verwenden, die von dem Anrufer zugeordnet. Nämlich, dass es auf dem Stapel zugeordnet wird, anstatt ein Haufen. Ich kann auch vorstellen, dass die Windows-Designer wollen nicht jeden Prozess zwingen, um eine Verknüpfung zu OleAut32.dll zu haben und den Preis für die Aufrechterhaltung der COM-Heap zu zahlen. Denken Sie daran, dass, wenn die meisten der Windows-API wurde die Leistungsmerkmale der typischen Hardware entworfen, war ganz anders als jetzt.

Ein weiterer möglicher Grund für nicht BSTR mit mehr ist weit verbreitet, dass der Windows-API in C ausgerichtet ist und die Lebensdauer von BSTR von C Verwaltung ist sehr viel komplizierter als von höheren Programmiersprachen wie C ++, C #, Delphi, etc.

Es gibt eine extra jedoch Komplikation. Das Delphi-ABI für WideString Rückgabewerte ist nicht kompatibel mit Microsoft-Tool. Sie sollten nicht WideString als Rückgabetyp verwenden, sondern bringen Sie es über einen out Parameter. Weitere Einzelheiten finden Sie unter Warum kann ein Wide nicht als Funktion Rückgabewert für Interop verwendet werden?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top