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傳過來的結構體數據了。

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