【问题标题】:C# / C++ passing struct got Access Violation ExceptionC#/C++ 传递结构得到访问冲突异常
【发布时间】:2021-05-11 22:55:55
【问题描述】:

早上好, 我正在尝试实现一个 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 作为值返回,它们工作得很好。

【问题讨论】:

  • 字段顺序不一样,可能是这个问题
  • 您也可以尝试使用FieldOffset 属性来明确字段的顺序。
  • char[] 应该是ByValTStr 而不是LPStr,您的字段顺序错误,您的SizeConst 也不正确

标签: c# c++ struct unmanaged cdecl


【解决方案1】:

只是猜测

在非托管方面,参数类型是 Host*,而在 C++ 中,您传递的是 &test_host

但是,在 C# 方面,您有一个 struct(默认情况下通过-a-copy 传递的东西,因此通过-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 在 cmets 中所说的内容 - 如果 C# 提供的字段顺序与预期的本机代码之间的字段顺序不同,这也可能导致相同的异常。

【讨论】:

  • char[] 应该是 ByValTStr 而不是 LPStr
  • ...[MarshalAs(UnmanagedType.ByValTStr)] string id,... VS 的智能感知让我遇到了这个错误:非托管类型 'ByValTStr' 仅对字段有效
  • @AndreaFausti 抱歉,我指的是char[] 的结构,你说得对,代表是char*,即LPStr
  • 解决方案是两个建议:ref 在委托声明中;结构声明中的“参数顺序”。非常感谢!!
猜你喜欢
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-07
  • 2016-03-27
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
相关资源
最近更新 更多