none
C#调用C++dll遇到个数据类型转换的问题 RRS feed

  • 问题

  • 这几天弄使用C#调用C++dll事情;遇到好多问题; 比如下面这个;

    这里有个回调函数,当有事件发生时,DLL内部会调用这个函数

    public static void rcv_alarm(IntPtr szData, Int32 lDataLen);

    szData是dll传递给C#的数据指针,后面的是这个数据的长度;

    这个数据包含一些结构体字节流,出问题的是下面这个结构体;

    这是C++原型定义

    //c++定义
    typedef struct ACC_EventSlice
    {			
    	int millisOffset;				
    	unsigned int snapshotLength;	
    	unsigned char* pSnapshot;		
    	unsigned int	direction;
    	unsigned char snapshotID[8];
    } ACC_EventSlice;

    //这是C#里面转换后的定义

    C#定义
    [StructLayout(LayoutKind.Sequential)]
    public struct ACC_EventSlice
    {
    	public int millisOffset;	
    	public UInt32 snapshotLength;//pSnapshot的数据长度
            [MarshalAs(UnmanagedType.LPArray)] 
    	public IntPtr pSnapshot;//这个字段在下面转换时会抛出异常		
    	public UInt32	direction;		
            [MarshalAs(UnmanagedType.ByValArray,SizeConst=8)]
    	public byte [] snapshotID;
    } ;

    当收到数据后,要将字节流还原为结构体;我用的下面这个函数

    //参数bytes数据地址指针
    //type 数据类型
    //iStartIndex 数据起始索引位置
    public static object BytesToStuct(byte[] bytes,Type type,int iStartIndex)
    {
          int size = Marshal.SizeOf(type);
          IntPtr structPtr = Marshal.AllocHGlobal(size);
          Marshal.Copy(bytes, iStartIndex, structPtr, size);
          object obj = Marshal.PtrToStructure(structPtr, type);//这里会抛出异常
          Marshal.FreeHGlobal(structPtr);
          return obj;
    }

    像这样转换

    ACC_EventSlice event = (ACC_EventSlice)BytesToStuct(dataBuf, typeof(ACC_EventSlice), iIndex);

    抛出的异常如下:

    无法封送处理类型为“ACC_EventSlice”的字段“pSnapshot”: 无效的托管/非托管类型组合(Int/UInt 必须与 SysInt 或 SysUInt 成对出现)。
    有没有老师能够指点一下呢?

    2013年12月18日 7:11

答案

  • unsigned int snapshotLength;
    unsigned char* pSnapshot;

    这两个字段表示分配了 snapshotLength 字节的内存,并将首地址保存到 pSnapshot 变量。

    所以,C# 里这样定义:

    public UInt32 snapshotLength;//pSnapshot的数据长度
    public IntPtr pSnapshot;//这个字段在下面转换时会抛出异常

    你也可以试下下面这种形式,如果不行,还是只能用上面这种。我记得应该还有种 Custom Marshal 的方式。

    public UInt32 snapshotLength;//pSnapshot的数据长度

    [MarshalAs(UnmanagedType.LPArray)]
    public byte[] pSnapshot;//这个字段在下面转换时会抛出异常

    看来是我画蛇添足了,去掉[MarshalAs(UnmanagedType.LPArray)] 果然就好了;只是不明白出现这个问题的根本原因;

    我再去看看Custom Marshal是个什么东西,感谢orz

    它的异常提示的意思是你没指定数组元素的类型,也就是你需要如下设置:

    [MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.U1)]

    但是问题就来了,指定为 UnmanagedType.LPArray 类型时,当从非托管到托管进行封送处理时,它会根据 SizeConst 和 SizeParamIndex 来确定数组的实际长度。(这些话将鼠标停留在 LPArray 上就能看到,一定要看代码的注释啊)

    因此下面的代码:

    [MarshalAs(UnmanagedType.ByValArray,SizeConst=8)]
    public byte [] snapshotID;

    正确,因为是定长数组。而 pSnapshot 字段就不行,即使指定了 SizeConst 也不行,因为这是变长数组,其长度由字段 snapshotLength 决定(如果 SizeConst 足够大的话,可能是正确的)。

    2013年12月18日 8:29

全部回复

  • unsigned int snapshotLength;
    unsigned char* pSnapshot;

    这两个字段表示分配了 snapshotLength 字节的内存,并将首地址保存到 pSnapshot 变量。

    所以,C# 里这样定义:

    public UInt32 snapshotLength;//pSnapshot的数据长度
    public IntPtr pSnapshot;//这个字段在下面转换时会抛出异常

    你也可以试下下面这种形式,如果不行,还是只能用上面这种。我记得应该还有种 Custom Marshal 的方式。

    public UInt32 snapshotLength;//pSnapshot的数据长度

    [MarshalAs(UnmanagedType.LPArray)]
    public byte[] pSnapshot;//这个字段在下面转换时会抛出异常

    2013年12月18日 7:23
  • unsigned int snapshotLength;
    unsigned char* pSnapshot;

    这两个字段表示分配了 snapshotLength 字节的内存,并将首地址保存到 pSnapshot 变量。

    所以,C# 里这样定义:

    public UInt32 snapshotLength;//pSnapshot的数据长度
    public IntPtr pSnapshot;//这个字段在下面转换时会抛出异常

    你也可以试下下面这种形式,如果不行,还是只能用上面这种。我记得应该还有种 Custom Marshal 的方式。

    public UInt32 snapshotLength;//pSnapshot的数据长度

    [MarshalAs(UnmanagedType.LPArray)]
    public byte[] pSnapshot;//这个字段在下面转换时会抛出异常

    看来是我画蛇添足了,去掉[MarshalAs(UnmanagedType.LPArray)] 果然就好了;只是不明白出现这个问题的根本原因;

    我再去看看Custom Marshal是个什么东西,感谢orz

    2013年12月18日 7:38
  • unsigned int snapshotLength;
    unsigned char* pSnapshot;

    这两个字段表示分配了 snapshotLength 字节的内存,并将首地址保存到 pSnapshot 变量。

    所以,C# 里这样定义:

    public UInt32 snapshotLength;//pSnapshot的数据长度
    public IntPtr pSnapshot;//这个字段在下面转换时会抛出异常

    你也可以试下下面这种形式,如果不行,还是只能用上面这种。我记得应该还有种 Custom Marshal 的方式。

    public UInt32 snapshotLength;//pSnapshot的数据长度

    [MarshalAs(UnmanagedType.LPArray)]
    public byte[] pSnapshot;//这个字段在下面转换时会抛出异常

    看来是我画蛇添足了,去掉[MarshalAs(UnmanagedType.LPArray)] 果然就好了;只是不明白出现这个问题的根本原因;

    我再去看看Custom Marshal是个什么东西,感谢orz

    它的异常提示的意思是你没指定数组元素的类型,也就是你需要如下设置:

    [MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.U1)]

    但是问题就来了,指定为 UnmanagedType.LPArray 类型时,当从非托管到托管进行封送处理时,它会根据 SizeConst 和 SizeParamIndex 来确定数组的实际长度。(这些话将鼠标停留在 LPArray 上就能看到,一定要看代码的注释啊)

    因此下面的代码:

    [MarshalAs(UnmanagedType.ByValArray,SizeConst=8)]
    public byte [] snapshotID;

    正确,因为是定长数组。而 pSnapshot 字段就不行,即使指定了 SizeConst 也不行,因为这是变长数组,其长度由字段 snapshotLength 决定(如果 SizeConst 足够大的话,可能是正确的)。

    2013年12月18日 8:29