Почему библиотеки DLL Delphi могут использовать WideString без использования ShareMem?

StackOverflow https://stackoverflow.com/questions/9331026

  •  27-10-2019
  •  | 
  •  

Вопрос

Ответ Дэвида на другой вопрос показывает 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;

Это означает, что вызывающий объект предоставляет буфер и максимальную длину.Библиотека DLL Windows выполняет запись в этот буфер с ограничением длины.Вызывающий объект выделяет и освобождает память.

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

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

Вот такой последующее наблюдение.

Это было полезно?

Решение

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top