Почему библиотеки Delphi DLL могут использовать WideString без ShareMem? - PullRequest
20 голосов
/ 17 февраля 2012

Ответ Дэвида на другой вопрос показывает функцию DLL Delphi, возвращающую 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 выполняет запись в этот буфер с ограничением длины.Вызывающая сторона выделяет и освобождает память.

Другой вариант заключается в том, что DLL выделяет память, например, с помощью LocalAlloc, а вызывающая сторона освобождает память, вызывая LocalFree.

Как распределение и освобождение памяти работает с моим примером DLL?Происходит ли "магия", потому что результат WideString (BSTR)?И почему API-интерфейсы Windows не объявляются с таким удобным соглашением?(Существуют ли какие-либо API-интерфейсы Win32, использующие такое соглашение?)


РЕДАКТИРОВАТЬ:

Я протестировал 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!

Вот продолжение .

1 Ответ

25 голосов
/ 17 февраля 2012

A WideString - это то же самое, что и BSTR, это просто имя Delphi.Распределение памяти обрабатывается общим распределителем COM, CoTaskMemAlloc.Поскольку все стороны используют один и тот же распределитель, вы можете безопасно распределить его в одном модуле, а освободить в другом.

Таким образом, причина, по которой вам не нужно использовать Sharemem, заключается в том, что куча Delphi не используется.Вместо этого используется куча COM.И это распределяется между всеми модулями процесса.

Если вы посмотрите на реализацию WideString в Delphi, вы увидите вызовы следующих API: SysAllocStringLen, SysFreeString и SysReAllocStringLen.Это предоставляемые системой BSTR функции API .

Многие из API-интерфейсов Windows, на которые вы ссылаетесь, предшествовали появлению COM.Более того, при использовании буфера фиксированной длины, выделяемого вызывающей стороной, есть преимущества в производительности.А именно, что он может быть размещен в стеке, а не в куче.Я также могу представить, что разработчики Windows не хотят заставлять каждый процесс ссылаться на OleAut32.dll и платить за поддержание кучи COM.Помните, что когда была разработана большая часть Windows API, характеристики производительности типичного оборудования сильно отличались от нынешних.

Другая возможная причина не использовать BSTR более широко заключается в том, что Windows API ориентирован на CИ управлять временем жизни BSTR из C намного сложнее, чем из языков более высокого уровня, таких как C ++, C #, Delphi и т. Д.

Однако есть дополнительные сложности.Delphi ABI для WideString возвращаемых значений несовместим с инструментами Microsoft.Не следует использовать WideString в качестве возвращаемого типа, вместо этого возвращайте его через параметр out.Подробнее см. Почему WideString не может использоваться как возвращаемое значение функции для взаимодействия?

...