我使用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中,astring
是托管类型。此外,对此类类型进行了一些不寻常的处理。实际上var
,它们在所有其他参数之后作为额外的隐式参数传递。C#代码通过寄存器传递返回值。
这意味着C#函数具有2个参数,而Delphi函数具有3个参数。那就是不匹配的原因。
无论如何,从C#返回一个字符串都会导致指向以null终止的字符数组的指针被编组。它当然不会作为Delphi字符串进行封送。
您有一些可用的解决方案:
PAnsiChar
。或者,PWideChar
如果将C#返回值编组为LPWStr
。您需要通过调用来释放指针CoTaskMemFree
StringBuilder
在C#方面。并传递缓冲区的长度。string
编组为的out参数UnmanagedType.BStr
。映射到WideString
Delphi中。调用方分配的缓冲区存在的问题是,要求调用方知道要分配多大的缓冲区。
引起细微差别的BStr/WideString
是Delphi的ABI与Microsoft的不兼容,请参见为什么不能将WideString用作互操作的函数返回值?您可以通过返回字符串作为out
参数而不是函数返回值来解决此问题。
返回以映射为的方式string
编组为的C#,将使您承担调用以释放内存的任务。总的来说,我想我会选择这个选项。这是该方法的示例。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;
}
}
}
德尔斐
{$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
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句