C# / C++ 传递结构得到访问冲突异常
C# / C++ passing struct got Access Violation Exception
早上好,
我正在尝试实现一个 pos 设备。本机 api 是一个 C++ 库,我必须在我的 C# 项目中使用它。
我已经导入了几乎所有需要的功能(工作正常),但其中有一个我无论如何也做不到。
这是 C++ 库中的函数:
__declspec(dllexport) void setHost(char* id, Host* host, char* trx, char* flag);
这是在 C++ 库中声明的结构:
typedef struct {
int hostConnectionType;
char ipAddress[15 + 1];
int tcpPort;
int hostTransportProtocol;
char gtId[5 + 1];
char gprsAPN[64 + 1];
char gprsUserName[15 + 1];
char gprsPassword[8 + 1];
int tlsCertificateId;
char personalizationId[10 + 1];
int gtIndex;
} Host;
这是 C++ 演示项目中的用法示例:
Host test_host;
test_host.hostConnectionType = 3;
test_host.hostTransportProtocol = 2;
test_host.gtIndex = 9;
strcpy(test_host.ipAddress, "199.188.177.166");
test_host.tcpPort = 54321;
strcpy(test_host.gtId, "12345");
test_host.tlsCertificateId = 21985;
strcpy(test_host.personalizationId, "001");
MobilePosAdapter_dll("00000123", &test_host, "0000000001", "1000");
我在 C# 项目中所做的是声明 Host 结构:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Host
{
public int hostConnectionType;
public int hostTransportProtocol;
public int tcpPort;
public int tlsCertificateId;
public int gtIndex;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string ipAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string gtId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string gprsAPN;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string gprsUserName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string gprsPassword;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string personalizationId;
}
然后,声明函数、属性和符号:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate void cdecl__setHost([MarshalAs(UnmanagedType.LPStr)] string id, Host Host, [MarshalAs(UnmanagedType.LPStr)] string trx, [MarshalAs(UnmanagedType.LPStr)] string flags);
private static cdecl__setHost _setHost = (cdecl__setHost)Marshal.GetDelegateForFunctionPointer(NativeMethods.GetProcAddress(DllPtr, "setHost"), typeof(cdecl__setHost));
最后测试一下:
Host host = new Host()
{
gtId = 12345,
gtIndex = 0,
hostConnectionType = 3,
hostTransportProtocol = 2,
ipAddress = "199.188.177.166",
personalizationId = "001",
tcpPort = 54321,
tlsCertificateId = 21985
};
_setHost("00000123", host, "0000000001", "1000");
我得到了一个System.AccessViolationException。
我哪里错了?
这是整个 API 中唯一一个将结构作为参数的函数。其他函数的值是 struct return,它们工作正常。
猜一猜
在非托管方面,参数类型是 Host*
,而在 C++ 中,您传递的是 &test_host
。
但是,在 C# 方面,您有一个 struct(默认情况下通过副本传递的东西,所以通过 val)并且您正在传递仅 host
到声明参数为:
的函数
private delegate
void cdecl __setHost(
[MarshalAs(UnmanagedType.LPStr)] string id,
Host host, // <--------- HERE
[MarshalAs(UnmanagedType.LPStr)] string trx,
[MarshalAs(UnmanagedType.LPStr)] string flags
);
请注意,您没有在 host
参数中添加任何 MarshalAs,所以我想它将通过复制传递,并且会发生一些损坏,因为接收函数期望获得一个指针(很少字节)而不是整个结构(多几倍)。
我要尝试的第一件事是添加您想要将其作为指针传递的任何提示,例如:
private delegate
void cdecl __setHost(
[MarshalAs(UnmanagedType.LPStr)] string id,
ref Host host, // <--------- HERE
[MarshalAs(UnmanagedType.LPStr)] string trx,
[MarshalAs(UnmanagedType.LPStr)] string flags
);
并称其为
_setHost("00000123", ref host, "0000000001", "1000");
此外,请务必仔细检查 Ackdari 在评论中所说的内容 - 如果 C# 提供的字段顺序与本机代码预期的字段顺序不同,也可能会导致相同的异常..
早上好, 我正在尝试实现一个 pos 设备。本机 api 是一个 C++ 库,我必须在我的 C# 项目中使用它。 我已经导入了几乎所有需要的功能(工作正常),但其中有一个我无论如何也做不到。
这是 C++ 库中的函数:
__declspec(dllexport) void setHost(char* id, Host* host, char* trx, char* flag);
这是在 C++ 库中声明的结构:
typedef struct {
int hostConnectionType;
char ipAddress[15 + 1];
int tcpPort;
int hostTransportProtocol;
char gtId[5 + 1];
char gprsAPN[64 + 1];
char gprsUserName[15 + 1];
char gprsPassword[8 + 1];
int tlsCertificateId;
char personalizationId[10 + 1];
int gtIndex;
} Host;
这是 C++ 演示项目中的用法示例:
Host test_host;
test_host.hostConnectionType = 3;
test_host.hostTransportProtocol = 2;
test_host.gtIndex = 9;
strcpy(test_host.ipAddress, "199.188.177.166");
test_host.tcpPort = 54321;
strcpy(test_host.gtId, "12345");
test_host.tlsCertificateId = 21985;
strcpy(test_host.personalizationId, "001");
MobilePosAdapter_dll("00000123", &test_host, "0000000001", "1000");
我在 C# 项目中所做的是声明 Host 结构:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Host
{
public int hostConnectionType;
public int hostTransportProtocol;
public int tcpPort;
public int tlsCertificateId;
public int gtIndex;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string ipAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
public string gtId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
public string gprsAPN;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string gprsUserName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
public string gprsPassword;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
public string personalizationId;
}
然后,声明函数、属性和符号:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate void cdecl__setHost([MarshalAs(UnmanagedType.LPStr)] string id, Host Host, [MarshalAs(UnmanagedType.LPStr)] string trx, [MarshalAs(UnmanagedType.LPStr)] string flags);
private static cdecl__setHost _setHost = (cdecl__setHost)Marshal.GetDelegateForFunctionPointer(NativeMethods.GetProcAddress(DllPtr, "setHost"), typeof(cdecl__setHost));
最后测试一下:
Host host = new Host()
{
gtId = 12345,
gtIndex = 0,
hostConnectionType = 3,
hostTransportProtocol = 2,
ipAddress = "199.188.177.166",
personalizationId = "001",
tcpPort = 54321,
tlsCertificateId = 21985
};
_setHost("00000123", host, "0000000001", "1000");
我得到了一个System.AccessViolationException。
我哪里错了?
这是整个 API 中唯一一个将结构作为参数的函数。其他函数的值是 struct return,它们工作正常。
猜一猜
在非托管方面,参数类型是 Host*
,而在 C++ 中,您传递的是 &test_host
。
但是,在 C# 方面,您有一个 struct(默认情况下通过副本传递的东西,所以通过 val)并且您正在传递仅 host
到声明参数为:
private delegate
void cdecl __setHost(
[MarshalAs(UnmanagedType.LPStr)] string id,
Host host, // <--------- HERE
[MarshalAs(UnmanagedType.LPStr)] string trx,
[MarshalAs(UnmanagedType.LPStr)] string flags
);
请注意,您没有在 host
参数中添加任何 MarshalAs,所以我想它将通过复制传递,并且会发生一些损坏,因为接收函数期望获得一个指针(很少字节)而不是整个结构(多几倍)。
我要尝试的第一件事是添加您想要将其作为指针传递的任何提示,例如:
private delegate
void cdecl __setHost(
[MarshalAs(UnmanagedType.LPStr)] string id,
ref Host host, // <--------- HERE
[MarshalAs(UnmanagedType.LPStr)] string trx,
[MarshalAs(UnmanagedType.LPStr)] string flags
);
并称其为
_setHost("00000123", ref host, "0000000001", "1000");
此外,请务必仔细检查 Ackdari 在评论中所说的内容 - 如果 C# 提供的字段顺序与本机代码预期的字段顺序不同,也可能会导致相同的异常..