【问题标题】:Memory Access Violation from a Delphi written DLL. Trying to access through DLLImport来自 Delphi 编写的 DLL 的内存访问冲突。尝试通过 DLLImport 访问
【发布时间】:2022-01-13 01:05:56
【问题描述】:

我需要使用硬件供应商提供的 Delphi 编写的 DLL。在提供的文件中,它提到了以下内容

int ReadCard(char *room, char *gate,char *stime, char *guestname, char *guestid, char *lift, char *track1, char *track2, long *cardno, int *st, int *Breakfast);

对参数的进一步阐述如下:

  • room [out]:字符串指针,接收返回的房间号, 推荐 10 个字节。

  • gate [out]:字符串指针,接收 返回的授权公共门,可以为NULL。

  • 客人姓名[出]: 字符串指针,接收返回的客人姓名,可以为NULL。

  • Guestid [out]:字符串指针,接收返回的guest ID, 可以为 NULL。

  • Lift [in]:电梯楼层,字符串参数,“00”代表 默认楼层,“99”代表授权所有楼层, 其他是指定的楼层代码,例如,“010205”代表 01,02,05 三层楼的授权。

  • track1 [out]:接收音轨 1条磁卡数据,可以为NULL。

  • track2 [out]:接收音轨 2 磁卡数据,可以为NULL。

  • Cardno [out]: 长整数 指针,接收返回的卡号,可以为NULL。

  • St [out]:整数 指针,接收返回卡状态:1-正常使用,3-正常 已擦除,4-丢失,5-损坏,6-自动擦除,可以为NULL。 早餐

  • [in]:早餐编号。

这是我的问题 - 在尝试了各种 DLLImport 方法后,我不断收到内存访问冲突错误。

错误内容类似于“System.AccessViolationException:尝试读取或写入受保护的内存这通常表明其他内存已损坏。” ntdll.dll 报错

以下是我尝试过的一些尝试:

    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder room,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder gate,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder guestName,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder guestID,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder lift,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder trackData1,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder trackData2,
        out Int32[] cardNumber,
        out int[] cardStatus,
        out int[] breakfast
    );
    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] string room,
        [MarshalAs(UnmanagedType.LPStr)] string gate,
        [MarshalAs(UnmanagedType.LPStr)] string stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] string guestName,
        [MarshalAs(UnmanagedType.LPStr)] string guestID,
        [MarshalAs(UnmanagedType.LPStr)] string lift,
        [MarshalAs(UnmanagedType.LPStr)] string trackData1,
        [MarshalAs(UnmanagedType.LPStr)] string trackData2,
        out long cardNumber,
        out long cardStatus,
        out long breakfast
    );

        [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        out char[] room,
        out char[] gate,
        out char[] stayPeriod,
        out char[] guestName,
        out char[] guestID,
        out char[] lift,
        out char[] trackData1,
        out char[] trackData2,
        out long[] cardNumber,
        out int[] cardStatus,
        out int[] breakfast
    );

    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] out string room,
        [MarshalAs(UnmanagedType.LPStr)] out string gate,
        [MarshalAs(UnmanagedType.LPStr)] out string stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] out string guestName,
        [MarshalAs(UnmanagedType.LPStr)] out string guestID,
        [MarshalAs(UnmanagedType.LPStr)] out string lift,
        [MarshalAs(UnmanagedType.LPStr)] out string trackData1,
        [MarshalAs(UnmanagedType.LPStr)] out string trackData2,
        IntPtr cardNumber,
        IntPtr cardStatus,
        IntPtr breakfast
    );

知道我错过了哪里吗?

【问题讨论】:

  • 您的意思是用 int32[] cardNumber 替换 long[] cardNumber?我也更新了我的问题。我已经尝试过 StringBuilder 选项,同样,获得内存访问冲突。我注意到 Delphi 是动态数组(索引从零开始)和静态数组,我相信他们的 DLL 使用的是动态数组。由于我们正在处理动态数组,因此分配 new StringBuilder(100) 会限制字符串增长吗?
  • 不相关,但我发现 ReadCard 函数没有任何参数来指定使用的输出缓冲区的大小并且文档甚至没有提到输出字符串的最大可能长度令人不安(除了房间号,它声明建议使用 10 个字节,但即使这听起来也不是保证,而仅仅是建议)。这是各种缓冲区溢出和安全漏洞的秘诀。看起来是个糟糕的设计。
  • @heapunderrun Delphi 风格的动态数组在这种情况下不起作用,所以不用担心。
  • @heapunderrun 同意,但另一方面,这些特定参数可能正在使用固定长度的缓冲区,其大小在其他地方进行了描述。
  • 除了所有其他的cmets,这里不需要unsafe

标签: c# c++ delphi dllimport


【解决方案1】:

您不能将string 用于需要指向char[] 缓冲区的指针的输出 参数。根据 Microsoft 的文档:Default Marshaling for Strings: Fixed-Length String Buffers,您需要改用 StringBuilder。对于 input 参数,string 可以正常工作。

在 C# 中,在 C/C++ 和 Delphi 中使用long(即Int64)作为int(32 位整数)参数是错误的。您需要改用int(即Int32)。另一方面,C/C++ 中的long 可能是 32 位或 64 位,具体取决于平台,因此您必须检查 DLL 实际使用的是什么。很有可能,它可能是 32 位的,假设是 Windows。

但是,为什么要将 cardNumbercardStatusbreakfast 参数声明为数组?文档或 C/C++ 声明中没有任何内容表明它们只是单个整数。 cardNumbercardStatus 应该是 out int (或者 也许 out long 对于 cardNumber),或者 IntPtr 如果你曾经为他们传递 nullbreakfast 只是输入,所以应该是简单的int

试试这样的:

[DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int ReadCard(
    StringBuilder room,
    StringBuilder gate,
    StringBuilder stayPeriod,
    StringBuilder guestName,
    StringBuilder guestID,
    string lift,
    StringBuilder trackData1,
    StringBuilder trackData2,
    out int cardNumber,
    out int cardStatus,
    int breakfast
);

当从 C# 调用它时,只需确保预先将 StringBuilder 参数分配到所需的长度,以便 DLL 有足够的内存来写入。例如:

StringBuilder roomSB = new StringBuilder(10);
ReadCard(roomSB, ...);
string room = roomSB.ToString();

【讨论】:

  • 感谢您的回答。有用。为了其他有这个问题的人的利益。为了您的信息,我在解决上述问题时遇到了另一个问题。另一个错误是“算术溢出”异常。如果您遇到类似的问题,它与 DLLImport 的使用方式无关。它与 Delphi DLL 重置 .net 浮点有关。这里讨论了stackoverflow.com/questions/2407040/…
猜你喜欢
  • 2011-05-28
  • 2013-03-31
  • 1970-01-01
  • 1970-01-01
  • 2012-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多