#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);
Комментариев нет:
Отправить комментарий