将字符串从 Delphi 传递到 C# returns null。但是,当我从 Delphi 调用 Delphi 库时,它工作正常。如何从 Delphi 接收字符串
Passing string from Delphi to C# returns null. However it works fine when I call the Delphi lib from Delphi. How to receive string from Delphi
我是 Delphi 的新手,一直在尝试为 .NET 制作一些 DLL。
我想要实现的是从我的 DLL 发送和接收 txt 输出。
这是我到目前为止所做的:
Delphi库函数:
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。但是当我尝试从 C# 调用它时,收到的输出为空。
这是我的 C# 代码块:
[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 代码与您的 C# 代码不兼容。
你的 C# 代码依赖于你的 DLL 不符合的 default string
marshaling behavior。
A string
默认情况下作为 PWideChar
指针传递给 DLL(如果您使用的是 Delphi 2009+,PChar
映射到 PWideChar
,否则它映射到 PAnsiChar
。
此外,您的 DLL 函数返回 PAnsiChar
,但编组程序默认期望返回 PWideChar
,因为您没有在 DLL 函数声明中应用 [return: MarshalAs(UnmanagedType.LPStr)]
属性在 C# 端。
但更重要的是,当 DLL returns 指向编组器随后获得所有权的内存指针时,必须使用 CoTaskMemAlloc()
或等价物分配内存,因为编组器释放内存默认为 CoTaskMemFree()
(参见 Memory management with the interop marshaler)。
您正在返回指向动态分配内存的指针,但是该内存不是使用 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
字符串而不是使用 CoTaskMemAlloc()
,只需确保在 C# 端将其编组为 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'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);
我是 Delphi 的新手,一直在尝试为 .NET 制作一些 DLL。
我想要实现的是从我的 DLL 发送和接收 txt 输出。
这是我到目前为止所做的:
Delphi库函数:
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。但是当我尝试从 C# 调用它时,收到的输出为空。
这是我的 C# 代码块:
[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 代码与您的 C# 代码不兼容。
你的 C# 代码依赖于你的 DLL 不符合的 default string
marshaling behavior。
A string
默认情况下作为 PWideChar
指针传递给 DLL(如果您使用的是 Delphi 2009+,PChar
映射到 PWideChar
,否则它映射到 PAnsiChar
。
此外,您的 DLL 函数返回 PAnsiChar
,但编组程序默认期望返回 PWideChar
,因为您没有在 DLL 函数声明中应用 [return: MarshalAs(UnmanagedType.LPStr)]
属性在 C# 端。
但更重要的是,当 DLL returns 指向编组器随后获得所有权的内存指针时,必须使用 CoTaskMemAlloc()
或等价物分配内存,因为编组器释放内存默认为 CoTaskMemFree()
(参见 Memory management with the interop marshaler)。
您正在返回指向动态分配内存的指针,但是该内存不是使用 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
字符串而不是使用 CoTaskMemAlloc()
,只需确保在 C# 端将其编组为 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'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);