来自 Delphi 写入的 DLL 的内存访问冲突。尝试通过 DLLImport 访问
Memory Access Violation from a Delphi written DLL. Trying to access through DLLImport
我需要使用硬件供应商提供的 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。
来宾名[out]:
字符串指针,接收返回的客人姓名,可以为NULL。
Guestid [out]:字符串指针,接收返回的客人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
);
知道我错过了什么吗?
您不能将 string
用于 output 需要指向 char[]
缓冲区的参数。根据 Microsoft 的文档,您需要使用 StringBuilder
:Default Marshaling for Strings: Fixed-Length String Buffers。对于 输入 参数,string
可以正常工作。
在C#中,在C/C++和Delphi中对int
(32位整数)参数使用long
(即Int64
)是错误的。您需要改用 int
(即 Int32
)。另一方面,C/C++ 中的long
可能是 32 位或 64 位,具体取决于平台,因此您必须检查 DLL 实际使用的是什么。很有可能,它 可能 是 32 位,假设 Windows。
但是,为什么要将 cardNumber
、cardStatus
和 breakfast
参数声明为数组?文档或 C/C++ 声明中没有任何内容表明它们不是单个整数。 cardNumber
和 cardStatus
应该是 out int
(或者 maybe out long
for cardNumber
),或者 IntPtr
如果你曾经为他们传递 null
。 breakfast
只是输入,所以它应该只是 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();
我需要使用硬件供应商提供的 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。
来宾名[out]: 字符串指针,接收返回的客人姓名,可以为NULL。
Guestid [out]:字符串指针,接收返回的客人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
);
知道我错过了什么吗?
您不能将 string
用于 output 需要指向 char[]
缓冲区的参数。根据 Microsoft 的文档,您需要使用 StringBuilder
:Default Marshaling for Strings: Fixed-Length String Buffers。对于 输入 参数,string
可以正常工作。
在C#中,在C/C++和Delphi中对int
(32位整数)参数使用long
(即Int64
)是错误的。您需要改用 int
(即 Int32
)。另一方面,C/C++ 中的long
可能是 32 位或 64 位,具体取决于平台,因此您必须检查 DLL 实际使用的是什么。很有可能,它 可能 是 32 位,假设 Windows。
但是,为什么要将 cardNumber
、cardStatus
和 breakfast
参数声明为数组?文档或 C/C++ 声明中没有任何内容表明它们不是单个整数。 cardNumber
和 cardStatus
应该是 out int
(或者 maybe out long
for cardNumber
),或者 IntPtr
如果你曾经为他们传递 null
。 breakfast
只是输入,所以它应该只是 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();