访问Delphi DLL抛出exceptionexception

当我调用Dll方法时,它有时会引发exception,有时则不会。

我这样称呼它:

public class DllTest { [DllImport(@"MyDll.dll")] public extern static string MyMethod(string someStringParam); } class Program { static void Main(string[] args) { DllTest.MyMethod("SomeString"); } } 

有时得到的例外是:

AccessViolationException

尝试读取或写入受保护的内存。 这通常表明其他内存已损坏。

有没有人知道我为什么有时会得到这个例外? 为什么有时会顺利运行?

您显然在p / invoke代码和Delphi代码之间存在不匹配。 您还没有显示Delphi代码,但C#代码足以知道Delphi代码应该是什么样子。

您的DllImport属性使用默认值来调用约定和字符集。 这意味着调用约定是stdcall ,字符集是ANSI。 您尚未指定任何编组属性,因此必须使用默认编组。

因此,您的Delphi代码必须如下所示:

 function MyMethod(someStringParam: PChar): PChar; stdcall; begin Result := ??; end; 

而现在这就是问题所在。 p / invoke marshaller以非常特殊的方式处理string返回值。 它假定p / invoke marshaller负责释放返回值的内存。 它必须使用与本机代码相同的分配器。 编组器的假设是将使用共享的COM分配器。

因此规则是本机代码必须通过调用CoTaskMemAlloc为COM分配器分配内存。 我敢打赌,你的代码不会这样做,这肯定会导致错误。

下面是一个示例,说明如何在代码中创建与C#签名一起使用的本机Delphi函数。

 function MyMethod(someStringParam: PChar): PChar; stdcall; var Size: Integer; begin Size := SizeOf(Char)*(StrLen(someStringParam)+1);//+1 for zero-terminator Result := CoTaskMemAlloc(Size); Move(someStringParam^, Result^, Size); end; 

虽然你可以采用这种方法,但我推荐一种替代方案。 将所有字符串BSTR为C#端的BSTR和Delphi端的WideString 。 这些是匹配类型,也由COM分配器分配。 双方都知道如何处理这些类型,并将使您的生活更轻松。

不幸的是,你无法通过互操作边界从Delphi函数返回WideString ,因为Delphi使用不同的ABI作为函数返回值。 有关此问题的更多详细信息,请参阅我的问题为什么WideString不能用作互操作的函数返回值?

因此,为了解决这个问题,我们可以将Delphi代码中的返回类型声明为TBStr 。 您的代码将如下所示:

C#

 [DllImport(@"MyDll.dll")] [return: MarshalAs(UnmanagedType.BStr)] private static extern string MyMethod( [MarshalAs(UnmanagedType.BStr)] string someStringParam ); 

delphi

 function MyMethod(someStringParam: WideString): TBStr; stdcall; begin Result := SysAllocString(POleStr(someStringParam)); end; 

对我来说,使用UnmanagedType.BStr将Delphi WideString编组为.Net字符串在In和Out参数的情况下非常有效。 但是在函数返回字符串的情况下它会失败。 我有一个Delphi函数 –

 function WS(val: WideString): WideString; stdcall; begin result := val; end; procedure WS1(out result: widestring); stdcall; begin result := 'ABCDE'; end; 

和通讯员.Net声明 –

 [DllImport(@"my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.BStr)] static extern string WS( [MarshalAs(UnmanagedType.BStr)] string val ); [DllImport("my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] static extern void WS1( [MarshalAs(UnmanagedType.BStr)] out string res); 

调用WS1()工作正常,而WS()引发exception。 例外取决于Delphi项目中包含的单位。 如果包含“SysUtils”或“Classes” – .Net应用程序引发SEHException“外部组件抛出exception”,如果两个单元都被排除,则应用程序显示“009C43B4运行时错误203”错误对话框并终止其执行。 顺便说一句,使用“ShareMem”单元不会改变任何东西。