Я совершенно новичок в Delphi и пытаюсь сделать несколько DLL для .NET.
Чего я хочу добиться, так это отправлять и получать текстовый вывод из моих DLL.
Вот что я сделал до сих пор:
Функция Delphi Library:
function DBConnet(inputStr: PChar; connStr: PChar): PAnsiChar; stdcall; export;
var
conStr: string;
s: string;
begin
inputStr := PChar('Hello from Delphi! How are you ' + inputStr + connStr);
try
Result := PAnsiChar(inputStr);
except
on e: Exception do
begin
Result := 'exception';
end;
end;
end;
Exports
DBConnet;
end.
Вот моя функция вызова в Delphi:
function DBConnet(inputStr: PChar; connStr: PChar): PChar; stdcall; external 'NewLib.dll';
procedure TUseDLLForm.functionxClick(Sender: TObject);
var
a: string;
conStr: string;
i: integer;
begin
a := 'firstname';
conStr := 'lastname';
ShowMessage(DBConnet(pchar(a), pchar(conStr)));
end;
Это прекрасно работает с Delphi в Delphi. Но когда я пытаюсь вызвать его из С#, получен вывод null.
Вот мой блок кода С#:
[DllImport("NewLib.dll",
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Unicode)]
public static extern void DBConnet(string inputString, string
connectionString, [MarshalAs(UnmanagedType.BStr)] out string dbStrObj);
И тогда в Main я называю это так:
DBConnet(inputString, connectionString, out dbStrObj);
Код DLL, который вы показали, НЕ совместим с вашим кодом С#.
Ваш код С# полагается на поведение маршалинга string
по умолчанию, которому не соответствует ваша DLL.
По умолчанию string
передается в DLL как указатель PWideChar
(если вы используете Delphi 2009+, PChar
сопоставляется с PWideChar
, в противном случае вместо этого он сопоставляется с PAnsiChar
).
Кроме того, ваша функция DLL возвращает PAnsiChar
, но PWideChar
ожидает PWideChar
по умолчанию, поскольку вы не применили [return: MarshalAs(UnmanagedType.LPStr)]
в объявлении функции DLL на стороне С#.
Но что еще более важно, когда DLL возвращает указатель на память, владельцем которой затем становится CoTaskMemAlloc()
, память ДОЛЖНА быть выделена с помощью CoTaskMemAlloc()
или эквивалентной ей, поскольку CoTaskMemFree()
освобождает память с помощью CoTaskMemFree()
по умолчанию (см. Управление памятью с помощью взаимодействие маршалер).
Вы возвращаете указатель на динамически распределенную память, однако эта память не выделяется с помощью CoTaskMemAlloc()
. Фактически, память фактически управляется компилятором Delphi и освобождается автоматически при выходе из функции. Итак, вы на самом деле возвращаете неверный указатель на С#.
На самом деле, вы даже не возвращаете указатель на С#! На стороне С# вы объявили 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;
С#:
[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;
С#:
[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
вместо использования CoTaskMemAlloc()
, просто обязательно CoTaskMemAlloc()
ее как BSTR
на стороне С#:
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 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;
С#:
[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;
С#:
[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);