5

我正在尝试使用以下签名调用 Delphi DLL 中的方法:

 function SMap4Ovr(const OverFileName       : ShortString    ;
                    const Aclay              : Integer        ;
                    const Acarbon            : Double         ;
                    out   errstr             : ShortString): WordBool;

我在 C# 中使用以下导入:

        [DllImport("SMap.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public static extern bool SMap4Ovr(
        string OverFileName,
        int Aclay,
        double Acarbon,
        out string errstr
        );

但我得到了一个 AccessViolationException。

我似乎能够调用 DLL 中的几个更简单的方法,这些方法具有字符串参数,但没有整数或双精度数。

我也尝试过 CallingConvention = CallingConvention.Cdecl 但这给了我同样的错误。

4

2 回答 2

12

在编写互操作代码时,接口的两边在各个方面都匹配是至关重要的。以下是双方必须达成一致的主要问题:

  1. 调用约定。
  2. 参数列表。
  3. 参数类型和语义。

第一个观察结果是您的调用约定不匹配。你有register德尔福方面和stdcallC# 方面。Delphiregister约定是 Delphi 私有的,因此您应该使用stdcall.

其次,您的字符串参数类型不匹配。Delphishortstring是一种数据类型,在 Delphi 2 发布时成为遗留数据类型,应该被视为上个世纪的遗物。它从来都不是一个有效的互操作类型,并且 p/invoke 框架中没有任何东西可以用来匹配它。虽然您可以尝试手动进行编组,但当有简单的解决方案可用时,这是根本不需要的大量工作。你应该试着忘记一切shortstring

您需要使用界面双方都可以使用的字符串类型。您可以使用以空字符结尾的 C 字符串,但更好更简单的选择是 Delphi 中的BSTRCOM WideString

所以,最终的结果如下。

德尔福

function SMap4Ovr(
    OverFileName: WideString;
    Aclay: Integer;
    Acarbon: Double;
    out errstr: WideString
): WordBool; stdcall;

C#

[DllImport("SMap.dll")]
public static extern bool SMap4Ovr(
    [MarshalAs(UnmanagedType.BStr)] 
    string OverFileName,
    int Aclay,
    double Acarbon,
    [MarshalAs(UnmanagedType.BStr)] 
    out string errstr
);

我没有费心在 上指定调用约定,DllImport因为默认值为stdcall. 如果您愿意,可以明确说明这一点。

使用时要小心WideString不要尝试将其用作返回值。因为 Delphi 对返回值使用非标准语义,所以您只能使用适合寄存器的简单类型作为返回值。

于 2013-05-17T06:55:20.927 回答
0

Delphi 中的默认调用约定是 register,而不是 stdcall。似乎调用约定细节向我们展示了 Microsoft fastcall 与 Borland fastcall 不同(注册)

并且 C# 字符串类型与 Delphi ShortString 不同(它内部包含一个字节长度 + 字符串主体)

于 2013-05-17T05:26:16.203 回答