Я неоднократно советовал людям использовать возвращаемое значение типа WideString
для целей взаимодействия.
- Доступ к Delphi DLL, бросающий исключение ocasional
- Веб-приложение ASP.NET, вызывающее Delphi DLL на веб-сервере IIS, блокируется при возврате строки PChar
- Почему DLL Delphi может использовать WideString без использования ShareMem?
Идея состоит в том, что a WideString
совпадает с BSTR
. Поскольку a BSTR
выделяется в общей куче COM-кластера, то нет проблем выделять в одном модуле и освобождать его в другом модуле. Это связано с тем, что все стороны согласились использовать ту же кучу, что и куча COM.
Однако, кажется, что WideString
не может использоваться как возвращаемое значение функции для взаимодействия.
Рассмотрим следующую DLL Delphi.
library WideStringTest;
uses
ActiveX;
function TestWideString: WideString; stdcall;
begin
Result := 'TestWideString';
end;
function TestBSTR: TBstr; stdcall;
begin
Result := SysAllocString('TestBSTR');
end;
procedure TestWideStringOutParam(out str: WideString); stdcall;
begin
str := 'TestWideStringOutParam';
end;
exports
TestWideString, TestBSTR, TestWideStringOutParam;
begin
end.
и следующий код С++:
typedef BSTR (__stdcall *Func)();
typedef void (__stdcall *OutParam)(BSTR &pstr);
HMODULE lib = LoadLibrary(DLLNAME);
Func TestWideString = (Func) GetProcAddress(lib, "TestWideString");
Func TestBSTR = (Func) GetProcAddress(lib, "TestBSTR");
OutParam TestWideStringOutParam = (OutParam) GetProcAddress(lib,
"TestWideStringOutParam");
BSTR str = TestBSTR();
wprintf(L"%s\n", str);
SysFreeString(str);
str = NULL;
TestWideStringOutParam(str);
wprintf(L"%s\n", str);
SysFreeString(str);
str = NULL;
str = TestWideString();//fails here
wprintf(L"%s\n", str);
SysFreeString(str);
Вызов TestWideString
с ошибкой:
Необработанное исключение в 0x772015de в BSTRtest.exe: 0xC0000005: место чтения нарушения доступа 0x00000000.
Аналогично, если мы попытаемся вызывать это из С# с p/invoke, у нас есть сбой:
[DllImport(@"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
Ошибка:
Необработанное исключение типа "System.Runtime.InteropServices.SEHException" произошло в ConsoleApplication10.exe
Дополнительная информация: Внешний компонент выбрал исключение.
Вызов TestWideString
через p/invoke работает как ожидалось.
Таким образом, использование pass-by-reference с параметрами WideString и отображение их на BSTR
выглядит очень хорошо. Но не для возвращаемых значений функции. Я тестировал это на Delphi 5, 2010 и XE2 и наблюдал такое же поведение во всех версиях.
Выполнение входит в Delphi и выходит из строя почти сразу. Назначение Result
превращается в вызов System._WStrAsg
, первая строка которого:
CMP [EAX],EDX
Теперь EAX
является $00000000
и, естественно, существует нарушение прав доступа.
Кто-нибудь может это объяснить? Я делаю что-то неправильно? Я необоснованно ожидаю, что значения функций WideString
будут жизнеспособными BSTR
s? Или это просто Дельфийский дефект?