Код DLL, который вы показали, НЕ совместим с вашим кодом C #.
Ваш код C # полагается на стандартное string
поведение маршалинга , которому ваша DLL не соответствует.
A string
передается в DLL как указатель PWideChar
по умолчанию (если вы используете Delphi 2009+, PChar
отображается на PWideChar
, в противном случае вместо этого отображается PAnsiChar
).
Кроме того, ваша функция DLL возвращает PAnsiChar
, но маршаллер ожидает PWideChar
по умолчанию, поскольку вы не применили атрибут [return: MarshalAs(UnmanagedType.LPStr)]
в объявлении функции DLL на стороне C #.
Но что еще более важно, когда DLL возвращает указатель на память, которой маршаллер становится владельцем, память ДОЛЖНА быть выделена с CoTaskMemAlloc()
или эквивалентным, так как маршаллер освобождает память с CoTaskMemFree()
по умолчанию(см. Управление памятью с помощью маршалера взаимодействия ).
Вы возвращаете указатель на динамически распределенную память, однако эта память не выделяется с помощью CoTaskMemAlloc()
.Фактически, память фактически управляется компилятором Delphi и освобождается автоматически при выходе из функции.Итак, вы фактически возвращаете неверный указатель на C #.
На самом деле, вы даже не возвращаете указатель на C #!На стороне C # вы объявили DLL как имеющую параметр out
, но такого параметра на стороне DLL нет!
С учетом всего сказанного, попробуйте что-то более похожее на это:
DLL:
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
function UnicodeStringToCoTaskMemStr(const s: UnicodeString): PWideChar;
var
Size: Integer;
begin
Size := (Length(s) + 1) * SizeOf(WideChar);
Result := PWideChar(CoTaskMemAlloc(Size));
if Result <> nil then
Move(PWideChar(s)^, Result^, Size);
end;
function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; export;
var
sInput: UnicodeString;
sConn: UnicodeString;
begin
try
sInput := inputStr;
sConn := connStr;
Result := UnicodeStringToCoTaskMemStr('Hello from Delphi! How are you ' + sInput + sConn);
except
Result := UnicodeStringToCoTaskMemStr('exception');
end;
end;
Приложение Delphi:
function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; external 'NewLib.dll';
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
procedure TUseDLLForm.functionxClick(Sender: TObject);
var
a: UnicodeString;
conStr: UnicodeString;
ret: PWideChar;
begin
a := 'firstname';
conStr := 'lastname';
ret := DBConnet(PWideChar(a), PWideChar(conStr));
if ret <> nil then
begin
try
ShowMessage(ret);
finally
CoTaskMemFree(ret);
end;
end else
ShowMessage('nil');
end;
C #:
[DllImport("NewLib.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string DBConnet(string inputString, string connectionString);
Или с использованием параметра out
вместо:
DLL:
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
function UnicodeStringToCoTaskMemStr(const s: UnicodeString): PWideChar;
var
Size: Integer;
begin
Size := (Length(s) + 1) * SizeOf(WideChar);
Result := PWideChar(CoTaskMemAlloc(Size));
if Result <> nil then
Move(PWideChar(s)^, Result^, Size);
end;
function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: PWideChar): boolean; stdcall; export;
var
sInput: UnicodeString;
sConn: UnicodeString;
begin
Result := False;
try
sInput := inputStr;
sConn := connStr;
outputStr := UnicodeStringToCoTaskMemStr('Hello from Delphi! How are you ' + sInput + sConn);
Result := outputStr <> nil;
except
end;
end;
Приложение Delphi:
function DBConnet(inputStr: PWideChar; connStr: PWideChar, out outputStr: PWideChar): boolean; stdcall; external 'NewLib.dll';
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
procedure TUseDLLForm.functionxClick(Sender: TObject);
var
a: UnicodeString;
conStr: UnicodeString;
ret: PWideChar;
begin
a := 'firstname';
conStr := 'lastname';
if DBConnet(PWideChar(a), PWideChar(conStr), ret) then
begin
try
ShowMessage(ret);
finally
CoTaskMemFree(ret);
end;
end else
ShowMessage('fail');
end;
C #:
[DllImport("NewLib.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool DBConnet(string inputString, string connectionString,
[MarshalAs(UnmanagedType.LPWStr)] out outputString string);
Кроме того, вы можете выделить возвращенную память как BSTR
string вместо использования CoTaskMemAlloc()
, просто обязательно выделите ее как BSTR
на стороне C #:
DLL:
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; export;
var
sInput: UnicodeString;
sConn: UnicodeString;
begin
try
sInput := inputStr;
sConn := connStr;
// the RTL's StringToOleStr() function returns a BSTR...
Result := StringToOleStr('Hello from Delphi! How are you ' + sInput + sConn);
except
Result := StringToOleStr('exception');
end;
end;
Приложение Delphi:
function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; external 'NewLib.dll';
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
procedure TUseDLLForm.functionxClick(Sender: TObject);
var
a: UnicodeString;
conStr: UnicodeString;
ret: WideString; // NOT UnicodeString!
begin
a := 'firstname';
conStr := 'lastname';
Pointer(ret) := DBConnet(PWideChar(a), PWideChar(conStr));
if ret <> '' then
ShowMessage(ret)
else
ShowMessage('nil');
end;
C #:
[DllImport("NewLib.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string DBConnet(string inputString, string connectionString);
Или, используя параметр out
:
DLL:
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: WideString): boolean; stdcall; export;
var
sInput: UnicodeString;
sConn: UnicodeString;
begin
Result := False;
try
sInput := inputStr;
sConn := connStr;
outputStr := 'Hello from Delphi! How are you ' + sInput + sConn;
Result := True;
except
end;
end;
Приложение Delphi:
function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: WideString): boolean; stdcall; external 'NewLib.dll';
// uncomment this if you are NOT using D2009+ ...
{
type
UnicodeString = WideString;
}
procedure TUseDLLForm.functionxClick(Sender: TObject);
var
a: UnicodeString;
conStr: UnicodeString;
ret: WideString;
begin
a := 'firstname';
conStr := 'lastname';
if DBConnet(PWideChar(a), PWideChar(conStr), ret) then
ShowMessage(ret)
else
ShowMessage('fail');
end;
C #:
[DllImport("NewLib.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool DBConnet(string inputString, string connectionString,
[MarshalAs(UnmanagedType.BStr)] out string outputStr);