Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Какой UnmanagedType соответствует UnicodeString из Delphi?

#c_sharp #delphi #net


Delphi (UnicodeString):

function ShowDelphiMsg(inputStr : UnicodeString) : UnicodeString; stdcall;
var a : UnicodeString;
begin
    ShowMessage(inputStr);
    a := 'Тест!';
    result := a;
end;


C#

[DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention
= CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static extern string ShowDelphiMsg([MarshalAs(UnmanagedType.LPWStr)] string
inputStr);


Такой вариант отлично передает из C# в Delphi строку, Delphi выводит строку. Но основная
проблема - ошибка при возврате строки из Delphi.

Впорос: Какой UnmanagedType соответствует UnicodeString из Delphi?

p.s. архитектура у обоих приложений x64.



Delphi (WideString):

function ShowDelphiMsg(inputStr : WideString) : WideString; stdcall;
var a : WideString;
begin
    ShowMessage(inputStr);
    a := 'Тест!';
    result := a;
end;


Так же вызывает ошибку при возврате значения.



Упростил функцию

C#:

 [DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention
= CallingConvention.StdCall)]
 [return: MarshalAs(UnmanagedType.BStr)]
 internal static extern string ShowDelphiMsg();


Delphi:

function ShowDelphiMsg() : WideString; stdcall;
var a : WideString;
begin
    a := 'Тест!';
    result := a;
end;


Та же ошибка.
    


Ответы

Ответ 1



Судя по коду UnmanagedType.LPWStr тут используется PWideChar, что есть указатель на WideString. Из функции возвращать надо именно WideString. UPD: Оказывается, из-за особенностей C# (?) нужно использовать вот такой способ: Delphi: function SomeFunction2(out OutVar: Widestring): BOOL; stdcall; begin OutVar := 'Hello'; Result := True; end; C# [DllImport(@"Test.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res); т.е. возвращать WideString через результат нельзя, а вот делать out параметр (но не var см. ответ Alex) - запросто. Нагуглено: https://stackoverflow.com/questions/9331026/why-can-delphi-dlls-use-widestring-without-using-sharemem

Ответ 2



Проблема состоит в том, что Delphi и Visual Studio расходятся во мнении как трактовать соглашение вызова stdcall. Любая функция, возвращающая сложный (в т.ч. автоуправляемый) тип: function SomeFunction: WideString; Трактуется Delphi как: procedure SomeFunction(var AResult: WideString); В то же время, Visual Studio трактует её либо как: procedure SomeFunction(out AResult: WideString); Либо как: function SomeFunction: Pointer; // действительно возврат через EAX/RAX (в зависимости от того, какой именно тип используется в Result - размера Pointer или больше) В результате из-за этих отличий мы либо получаем Access Violation, либо утечку памяти. Варианты правильных решений: (Самый правильный) Использовать safecall вместо stdcall (Допустимый - ответ zed) Явно описывать выходной параметр на Delphi (Хак - ответ Alexis) Привести Result к простому типу и вручную управлять им Если интересно, можно почитать.

Ответ 3



В процессе изучения нашел еще один вариант, более приемлемый на мой взгляд: Delphi: uses ActiveX; function GetWideString(str: WideString): TBStr; stdcall; begin result := SysAllocString(POleStr(str)); end; C# [DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.BStr)] internal static extern string GetWideString([MarshalAs(UnmanagedType.BStr)]string inputString);

Комментариев нет:

Отправить комментарий