[英]Using a C# DLL in Delphi only uses the first function parameter
我使用C#DLL导出(UnmanagedExports- https: //www.nuget.org/packages/UnmanagedExports)使托管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;
}
Delphi部分(调用部分):
SomeCall: function(data1: PWideChar; data2: PWideChar;): String StdCall;
procedure DoSomeDLLWork(data1: PWideChar; data2: PWideChar);
var
dllCallResult: String;
begin
dllCallResult := SomeCall(data1,data2);
end
在这种情况下的问题是仅填充了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)
德尔斐(来电显示):
SomeCall: function(data1: PWideChar; data2: PWideChar;): boolean StdCall;
现在,我必须考虑一个返回值或一个缓冲区,以将结果字符串返回给delphi。
编辑2:
我同意David Heffernan提出的使用out参数的建议:
德尔福:
SomeCall: procedure(data1: PWideChar; data2: PWideChar; var result: PWideChar)StdCall;
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)
问题是string
返回值。 在Delphi中, string
是托管类型。 此外,对此类类型进行了一些不寻常的处理。 实际上,它们在所有其他参数之后作为额外的隐式var
参数传递。 C#代码通过寄存器传递返回值。
这意味着C#函数具有2个参数,而Delphi函数具有3个参数。 那就是不匹配的原因。
无论如何,从C#返回一个字符串都会导致指向以null结尾的字符数组的指针被编组。 它当然不会作为Delphi字符串进行封送。
您有一些可用的解决方案:
PAnsiChar
。 或PWideChar
,如果你当元帅的C#返回值LPWStr
。 您需要通过调用CoTaskMemFree
释放指针 StringBuilder
。 并传递缓冲区的长度。 string
类型的out参数,将其编组为UnmanagedType.BStr
。 映射到Delphi中的WideString
。 调用方分配的缓冲区的问题在于,要求调用方知道要分配的缓冲区大小。
BStr/WideString
的细微差别在于Delphi的ABI与Microsoft的不兼容,请参见为什么不能将WideString用作互操作的函数返回值? 您可以通过将字符串作为out
参数而不是函数返回值返回来解决此问题。
返回映射为PWideChar
,以LPWStr
编组的C# string
,使您LPWStr
调用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;
}
}
}
德尔菲
{$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.
输出量
foobar
不要将string
用作结果类型:该类型是Delphi专用的。
最简单的方法是使用BSTR
封送处理,它映射Delphi WideString
类型。
所以你定义
SomeCall: function(const aFileName, data2: WideString): WideString; StdCall;
可以这样映射:
[DllExport(CallingConvention = CallingConvention.StdCall)]
public static void AddCertificate([MarshalAs(UnmanagedType.BStr)] string data1, [MarshalAs(UnmanagedType.BStr)] string data2, [MarshalAs(UnmanagedType.BStr)] out string Result);
(在回答之后)技巧是转换作为附加out
参数返回的Delphi函数 ,如参数编组的Delphi低层信息所述 。
您必须封送返回值
像这样:
[DllExport(CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static string AddCertificate([MarshalAs(UnmanagedType.LPWStr)] string aFileName, [MarshalAs(UnmanagedType.LPWStr)] string aPassword)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.