C#调用C/C++动态链接库笔记(三)

主要分享结构体,联合体,结构体内嵌套结构体、联合体的调用经历:

1C中的结构体的定义:

在实例中,下面的底层大佬给我的dll中定义是这样的:

//枚举
typedef enum {
    Reader_NULL = 0x00,
    Reader_Mobile_UWB = 0x01,
    Reader_Shoulder_Rollcall = 0x02,
    Reader_Handset_UWB = 0x03,
    Reader_Handset_Rollcall = 0x04,
    Reader_Sentry_UWB = 0x10,
    Reader_Bluetooth = 0xF0,
} ReaderType;

//结构体
typedef struct {

    ReaderType readerType;

    uint32_t readerID;
    uint32_t tagID;
    uint32_t sequence;                  // 序号
    uint8_t  protoVer;                  // 协议版本号

    uint32_t addr;                      // IN_ADDR
    uint64_t time;
	//嵌套匿名联合体
    union {
    //嵌套结构体
       struct {
       	uint8_t mode;
     	 	uint16_t distance;          // 单位:厘米
            float voltage;              // 单位:伏特
            bool isMove;
          	 bool isCut;
            uint8_t version;            // 硬件版本号
       } TagStatus;
        struct {
            int result;                // 读取、设置配置时,通过此值判断是否成功
            uint8_t addr;
            int32_t value;
            int errcode;
        } ConfigStatus;
   
    };
} DevMsg;

这个结构体分析下来是这样的

struct
	  {
		(一堆变量的声明)	
		union	{
					struct(1)
					struct(2)
				}
		}

联合体我们知道是公用地址,也就是说union可以看成是一个结构体成员,在定义时,将struct(1)和struct(2)的存储地址相等就可以做出联合体的效果,至于结构体,在C#中本身就可以直接定义:

2下面是C#的代码:

   public enum ReaderType
        {
            Reader_NULL = 0x00,
            Reader_Mobile_UWB = 0x01,
            Reader_Shoulder_Rollcall = 0x02,
            Reader_Handset_UWB = 0x03,
            Reader_Handset_Rollcall = 0x04,
            Reader_Sentry_UWB = 0x10,
            Reader_Bluetooth = 0xF0,
        };
        public struct DevMsg
        {
            public DevType device;
            public ReaderType readerType;
            public TagType tagType;

            public MsgType type;

            public UInt32 readerID;
            public UInt32 tagID;
            public UInt32 sequence;                  // 序号
            public byte protoVer;                  // 协议版本号

            public UInt32 addr;                      // IN_ADDR
            public UInt64 time;
            public  TagStatus1 TagStatus;
     		public ConfigStatus1 ConfigStatus;
        };
        [StructLayout(LayoutKind.Explicit)]//重要
        public  struct TagStatus1
        {
     		[FieldOffset(0)]//重点
            public UInt16 distance;          // 单位:厘米
            [FieldOffset(2)]
            public byte modex;
            [FieldOffset(4)]
            public Single voltage;              // 单位:伏特
            [FieldOffset(8)]
            byte isMove;
            [FieldOffset(12)]
            byte isCut;
            [FieldOffset(16)]
            byte version;            // 硬件版本号
        };
            [StructLayout(LayoutKind.Explicit)]
        public struct ConfigStatus1
        {
       		 [FieldOffset(0)]
            Int16 result;                // 读取、设置配置时,通过此值判断是否成功
            [FieldOffset(0)]
            byte addr;
            [FieldOffset(0)]
            Int32 value;
            [FieldOffset(0)]
            Int16 errcode;
        };

首先写两点疑问:
1:为什么要用分配存储地址的方式?
因为在union中,赋值不是通过名字的方式赋值的,而是通过存储位置来给值得。
2:为什么代码中的fieldoffset给的地址如此凌乱?
因为经过多次测试,发现作者琢磨不透其的传值规律,有时byte占2位,有时又占4位,所以一不做二不休,咱们直接去看内存里面是咱们传值的,我们再来把这个地址填上。

3C#获得变量地址:

        int size = Marshal.SizeOf(msg);
        IntPtr intPtr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(msg, intPtr, true);

这个时候intptr就是我们结构体的地址了。

4C#查看变量内存

在运行时,点击 调试->窗口->内存 内存1-4随便选一个都可以
接着把你刚刚获得的地址输入进去,尽可以看到内存中存储的数据了

在这里插入图片描述
就是如上图所示,根据dll的协议,对照着,就可以完美的接受dll传过来的结构体数据了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章