C#調用C++dll文件獲取數據得到亂碼的解決方法

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">      很久沒寫博客了,最近空閒下來,有空整理了下最近幾個項目,把遇到的一些問題記錄下來。</span>

       做一個視頻二次開發的項目,是與大華的dss平臺進行對接。在獲取攝像頭名稱的時候經常出現亂碼,剛開始以爲是編碼問題,測試了各種編碼方式,最後都沒有解決。

       最終沒辦法,最後諮詢大華研發,因爲對方只會C++,而我們的項目是C#開發,雙方在代碼上面溝通了很久,才發現是因爲結構體初始化後,要給字段內存賦一個空值。

      因爲C#的習慣,每次new過後,就默認會賦值。所有也沒考慮到這麼多。先將解決代碼貼出來,大家也可以借鑑。

      C++結構體原型:

      

//獲取組織請求信息
 typedef struct tagGetDepInfo 
            {
	            IN char								szCoding[DPSDK_CORE_DGROUP_DGPCODE_LEN];	// 節點code
	            IN uint32_t							nDepCount;									// 組織個數
	            OUT Dep_Info_t*						pDepInfo;									// 組織信息,在外部創建,如果爲NULL則只返回個數
	            IN uint32_t							nDeviceCount;								// 設備個數
	            OUT Device_Info_Ex_t*				pDeviceInfo;								// 設備信息
            }Get_Dep_Info_t; 

 注意:其中Dep_Info_t*是一個集合,類似與C#的List<Dep_Info_t>,我們在C#中定義爲IntPtr指針地址。

下面是C#的定義(C#如何訪問C++的dll接口,請大家自行百度。一般是DllImport):

 /// <summary>
        /// 組織結構信息結構體
        /// </summary>
        //[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct Get_Dep_Info_t
        {
            /// <summary>
            /// IN char								szCoding[DPSDK_CORE_DGROUP_DGPCODE_LEN];	// 節點code
            /// </summary>
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = DPSDK_CORE_DGROUP_DGPCODE_LEN)]
            public byte[] szCoding;


            /// <summary>
            /// IN uint32_t							nDepCount;									// 組織個數
            /// </summary>
            public UInt32 nDepCount;

            /// <summary>
            /// OUT Dep_Info_t*						pDepInfo;									// 組織信息,在外部創建,如果爲NULL則只返回個數
            /// </summary>
            public IntPtr pDepInfo;
            //public List<Dep_Info_t> pDepInfo;


            /// <summary>
            /// IN uint32_t							nDeviceCount;								// 設備個數
            /// </summary>
            public UInt32 nDeviceCount;

            /// <summary>
            ///  OUT Device_Info_Ex_t*				pDeviceInfo;								// 設備信息
            /// </summary>
            public IntPtr pDeviceInfo;
            //public List<Device_Info_Ex_t> pDeviceInfo;
        }

注意:剛開始我們也是定義爲List集合,但是全部是亂碼,遇到這種數據類型不知道如何轉換的,大家都可以定義爲指針地址,然後再去轉換。因爲數據都是存放在內存中。

      上面貼出了C++與C#的結構體定義,下面就來說說C++與C#如何初始化結構體。也許大家都會說直接new就可以了,對這樣是沒錯的,但是並沒有這麼簡單。

   C++結構體初始化代碼

Get_Dep_Info_t* pGetDepInfo = new Get_Dep_Info_t;
	memset(pGetDepInfo, 0 , sizeof(Get_Dep_Info_t));
	memcpy(pGetDepInfo->szCoding, szDepId, strlen(szDepId));

	pGetDepInfo->nDepCount = pGetCountInfo->nDepCount;
	pGetDepInfo->nDeviceCount = pGetCountInfo->nDeviceCount;

	if (pGetDepInfo->nDepCount > 0)
	{
		pGetDepInfo->pDepInfo = new Dep_Info_t[pGetCountInfo->nDepCount];
		memset(pGetDepInfo->pDepInfo, 0, sizeof(Dep_Info_t)*pGetCountInfo->nDepCount);
	}

	if (pGetDepInfo->nDeviceCount > 0)
	{
		pGetDepInfo->pDeviceInfo = new Device_Info_Ex_t[pGetCountInfo->nDeviceCount];
		memset(pGetDepInfo->pDeviceInfo, 0, sizeof(Device_Info_Ex_t)*pGetCountInfo->nDeviceCount);
	}

      pGetDepInfo->pDepInfo = new Dep_Info_t[pGetCountInfo->nDepCount];
memset(pGetDepInfo->pDepInfo, 0, sizeof(Dep_Info_t)*pGetCountInfo->nDepCount);

memset給結構體賦一個0的空值。關鍵就是這個,當初轉換層C#時,就是唄這個地方整瘋掉了。

 

下面是C#的初始化代碼:

//組織和設備獲取接口調用順序:
            //0、DPSDK_LoadDGroupInfo
            //1、DPSDK_GetDGroupCount
            //2、DPSDK_GetDGroupInfo
            //3、DPSDK_GetChannelInfo

            //0、加載組織信息DPSDK_LoadDGroupInfo

            //1、獲取組織下子組織和子設備的個數,加載組織結構完成後才能使用
            DaHuaNetSdk_DSS_Szdjq_PInvoke.Get_Dep_Count_Info_t stuGetDepCountInfo = new DaHuaNetSdk_DSS_Szdjq_PInvoke.Get_Dep_Count_Info_t();
            stuGetDepCountInfo.szCoding = szDepId;
            stuGetDepCountInfo.nDepCount = 0;
            stuGetDepCountInfo.nDeviceCount = 0;

            lError = DaHuaNetSdk_DSS_Szdjq_PInvoke.DPSDK_GetDGroupCount(m_nPDLLHandle, ref stuGetDepCountInfo);
//2、獲取組織下子組織和子設備的信息,加載組織結構完成後才能使用
            if (lError == (Int32)DaHuaNetSdk_DSS_Szdjq_PInvoke.dpsdk_ErrorCode_DaHua.DPSDK_RET_SUCCESS)
            {
                DaHuaNetSdk_DSS_Szdjq_PInvoke.Get_Dep_Info_t stuGetDepInfo = new DaHuaNetSdk_DSS_Szdjq_PInvoke.Get_Dep_Info_t();
                stuGetDepInfo.szCoding = szDepId;

                //獲取部門個數,並初始化數組
                stuGetDepInfo.nDepCount = stuGetDepCountInfo.nDepCount;

                int tagDepInfotSize = Marshal.SizeOf(typeof(DaHuaNetSdk_DSS_Szdjq_PInvoke.Dep_Info_t));/* 獲取Dep_Info_t結構體長度 */
                IntPtr ptrDepInfoList = Marshal.AllocHGlobal(Convert.ToInt32(tagDepInfotSize * stuGetDepCountInfo.nDepCount));
                //Marshal.WriteInt32(ptrDepInfoList, 0);

                #region 初始化取值,組織機構指針

                for (int idx = 0; idx < stuGetDepInfo.nDepCount; idx++)
                {
                    IntPtr pBufferFlag = new IntPtr(ptrDepInfoList.ToInt32() + idx * tagDepInfotSize);
                    DaHuaNetSdk_DSS_Szdjq_PInvoke.Dep_Info_t pstDepInfoItem = new DaHuaNetSdk_DSS_Szdjq_PInvoke.Dep_Info_t();
                    pstDepInfoItem.szCoding = new byte[DaHuaNetSdk_DSS_Szdjq_PInvoke.DPSDK_CORE_DGROUP_DGPCODE_LEN];
                    pstDepInfoItem.szDepName = new byte[DaHuaNetSdk_DSS_Szdjq_PInvoke.DPSDK_CORE_DGROUP_DGPNAME_LEN];

                    Marshal.StructureToPtr(pstDepInfoItem, pBufferFlag, true);
                }

                #endregion
                stuGetDepInfo.pDepInfo = ptrDepInfoList;

                //獲取設備個數,並初始化數組
                stuGetDepInfo.nDeviceCount = stuGetDepCountInfo.nDeviceCount;
}

我們將Dep_Info_t*定義爲指針地址,然後將指針轉換成結構體,然後將結構體new,並將結構體中的byte數組賦0的初始值,當初就因爲byte沒有賦值0,導致內存中有髒數據,最終轉換出來的字符串變成了亂碼。



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