我在Delphi“a.dll”中创建了第三方DLL(无源代码)。
这个DLL有一个带有此签名的方法。
function GetAny(pFileName: String): String;
我不能用c#进行互操作调用,因为'String type'在delphi中有私有访问权。
所以在delphi中构建另一个DLL以封装该调用。
function GetAny(pFileName: String): String; external 'a.dll'
function GetWrapper(url : PChar) : PChar; stdcall;
begin
Result := PChar(GetAny(url)); // I need avoid this String allocation, is throwing a exception.
end;
[DllImport("wrapper.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr GetWrapper(String url);
在“GetWrapper”内部我调用外部“GetAny”,结果没问题(在delphi中我可以调试),但在我将此结果返回到c#侧之前,它会抛出异常。
IntPtr test = GetWrapper("a");
String result = Marshal.PtrToStringAnsi(test);
答案 0 :(得分:3)
您的包装器DLL也无法调用GetAny,因为string是托管的Delphi类型,无法跨模块边界传递。
问题是GetAny的返回值是在一个模块中分配的,并在另一个模块中解除分配。它在实现GetAny的DLL中分配,并在调用GetAny的DLL中解除分配。由于这两个DLL使用不同的内存管理器,因此您最终会尝试释放在不同堆上分配的内存。
如果可以说服实现GetAny的DLL共享内存管理器,那么您可以轻松解决该问题。
我确实质疑你提出的事实。按照目前的情况,除非DLL被设计为与ShareMem一起使用,否则永远不能安全地调用该函数。
如果您准备泄漏内存,可以试试这个:
<强>的Delphi 强>
function GetAny(pFileName: string): PChar; external 'a.dll'
procedure GetWrapper(url: PChar; out value: WideString); stdcall;
var
P: PChar;
begin
P := GetAny(url);
if Assigned(P) then
Value := P
else
Value := '';
end;
<强> C#强>
[DllImport("wrapper.dll"]
public static extern void GetWrapper(
string url,
[MarshalAs(UnmanagedType.BStr)]
out string value
);
答案 1 :(得分:0)
我已下载您的代码......
解决方案可能是这样的:
使用带有2个PChar类型参数的“cdecl”声明在Delphi中创建一个包装程序
原始Delphi功能:
function GetAny(pFileName: String): String; external 'a.dll';
Delphi - 包含函数的DLL:
procedure GetWrapper (url: PChar; var urlNew: PChar) cdecl;
var str: string;
begin
urlStr = string(url);
urNewStr := GetAny(urlStr);
urlNew := PChar(urNewStr);
end;
exports
GetWrapper;
begin
end.
使项目x32位(不是样本中的x64)
[DllImport("wrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetWrapper ([MarshalAs(UnmanagedType.LPStr)]string url, [MarshalAs(UnmanagedType.LPStr)] out string urlNew);
使用C#调用:
string fileName; // = @“wertwertwertwertwer”;
GetWrapper(“2.jpg”,out fileName);
Console.WriteLine(文件名);
在我的环境中它起作用了。 (Delphi 5和VS2012)。
希望它也适合你。