将字符串从 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);