fru*_*ugi 1 c# delphi dll dllexport delphi-xe2
我使用 C# DLL 导出(UnmanagedExports - https://www.nuget.org/packages/UnmanagementExports)使我的托管 C# DLL 可被非托管代码(如 Delphi)访问。我的问题是只有第一个函数参数从 delphi 传输到 C# dll:
C# DLL 部分
[DllExport("SomeCall", CallingConvention.StdCall)]
public static String SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2)
{
//Data1 is never filled with some string data.
String result = WorkWithData(data1);
//Data2 is filled with some string data.
result += WorkWithData(data2)
return result;
}
Run Code Online (Sandbox Code Playgroud)
Delphi部分(调用部分):
SomeCall: function(data1: PWideChar; data2: PWideChar;): String StdCall;
procedure DoSomeDLLWork(data1: PWideChar; data2: PWideChar);
var
dllCallResult: String;
begin
dllCallResult := SomeCall(data1,data2);
end
Run Code Online (Sandbox Code Playgroud)
本例的问题是只填充了data2。data1 永远不会被填充。我已经尝试过 StdCall 和 Cdecl。
编辑:
以下内容有效(data1 和 data2 已正确传输)-返回值从字符串更改为布尔值:
C#(DLL 部分):
[DllExport("SomeCall", CallingConvention.StdCall)]
public static bool SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2)
Run Code Online (Sandbox Code Playgroud)
德尔福(调用者):
SomeCall: function(data1: PWideChar; data2: PWideChar;): boolean StdCall;
Run Code Online (Sandbox Code Playgroud)
现在我必须考虑一个返回值或一个缓冲区来将结果字符串返回到delphi。
编辑2:
我采纳了 David Heffernan 使用 out 参数的建议:
德尔福:
SomeCall: procedure(data1: PWideChar; data2: PWideChar; var result: PWideChar)StdCall;
Run Code Online (Sandbox Code Playgroud)
C#
[DllExport("SomeCall", CallingConvention.StdCall)]
public static bool SomeCall([MarshalAs(UnmanagedType.LPWStr)] String data1, [MarshalAs(UnmanagedType.LPWStr)] String data2, [MarshalAs(UnmanagedType.LPWStr)] out String result)
Run Code Online (Sandbox Code Playgroud)
问题是string返回值。在 Delphi 中,astring是托管类型。此外,此类类型的待遇有些不寻常。它们实际上是在所有其他参数之后作为额外的隐式var参数传递的。C# 代码通过寄存器传递返回值。
这意味着 C# 函数有 2 个参数,但 Delphi 函数有 3 个参数。这就是解释这种行为的不匹配。
在任何情况下,从 C# 返回字符串都会导致指向正在编组的以 null 结尾的字符数组的指针。它当然不会编组为 Delphi 字符串。
您有一些可用的解决方案:
PAnsiChar. 或者,PWideChar如果您将 C# 返回值封送为LPWStr. 您需要通过调用来释放指针CoTaskMemFreeStringBuilderC# 方面。并传递缓冲区的长度。string,编组为UnmanagedType.BStr。这映射到WideString德尔福。调用者分配缓冲区的问题在于,要求调用者知道要分配多大的缓冲区。
细微差别BStr/WideString是 Delphi 的 ABI 与 Microsoft 的不兼容,请参阅为什么 WideString 不能用作互操作的函数返回值?您可以通过返回字符串作为out参数而不是函数返回值来解决此问题。
返回一个 C# string,编组为LPWStr,映射到,让您完成调用以释放内存的PWideChar任务。CoTaskMemFree总的来说,我想我会选择这个选项。这是该方法的一个示例。
C#
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace ClassLibrary1
{
public class Class1
{
[DllExport]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string Concatenate(
[MarshalAs(UnmanagedType.LPWStr)] string str1,
[MarshalAs(UnmanagedType.LPWStr)] string str2
)
{
return str1 + str2;
}
}
}
Run Code Online (Sandbox Code Playgroud)
德尔福
{$APPTYPE CONSOLE}
uses
Winapi.ActiveX; // for CoTaskMemFree
const
dllname = 'ClassLibrary1.dll';
function Concatenate(str1, str2: PWideChar): PWideChar; stdcall; external dllname;
procedure Main;
var
res: PWideChar;
str: string;
begin
res := Concatenate('foo', 'bar');
str := res;
CoTaskMemFree(res);
Writeln(Str);
end;
begin
Main;
Readln;
end.
Run Code Online (Sandbox Code Playgroud)
输出
富巴
| 归档时间: |
|
| 查看次数: |
3692 次 |
| 最近记录: |