2

我对 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);
4

1 回答 1

9

您显示的 DLL 代码与您的 C# 代码不兼容。

您的 C# 代码依赖于您的 DLL 不符合的默认string封送处理行为。

默认情况下, Astring作为PWideChar指针传递给 DLL(如果您使用的是 Delphi 2009+,则PChar映射到PWideChar,否则它会映射到PAnsiChar)。

此外,您的 DLL 函数返回 a PAnsiChar,但编组器PWideChar默认期望 a ,因为您没有[return: MarshalAs(UnmanagedType.LPStr)]在 C# 端的 DLL 函数声明上应用属性。

但更重要的是,当 DLL 返回指向内存的指针时,编组器将拥有该内存的所有权,内存必须分配CoTaskMemAlloc()或等效,因为编组器默认释放内存CoTaskMemFree()(请参阅使用互操作编组器进行内存管理)。

您正在返回一个指向动态分配内存的指针,但是该内存不是用CoTaskMemAlloc(). 事实上,内存实际上由 Delphi 编译器管理,并在函数退出时自动释放。因此,您实际上是在返回一个指向 C# 的无效指针。

事实上,您甚至没有返回指向 C# 的指针!在 C# 端,您已将 DLL 声明为具有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): 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;

德尔福应用程序:

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参数:

动态链接库:

// 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;

德尔福应用程序:

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字符串而不是 using ,只需确保在 C# 端CoTaskMemAlloc()将其编组为 a :BSTR

动态链接库:

// 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;

德尔福应用程序:

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参数:

动态链接库:

// 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;

德尔福应用程序:

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);
于 2018-11-29T00:56:06.067 回答