在 上篇文章重點講解數據類型的轉換,在此基礎上再講解函數調用。c++
一、函數調用c#
c++中函數數組
INF_NET_API INF_RESULT WINAPI INF_NET_GetList(long lLoginHandle,DWORD dwInfoContol,BYTE** pBuf,LONG& lBufSize);
C# 中函數
ide
[DllImport(_strCoreSDK)] public static extern int INF_NET_GetList(Int32 lLoginHandle, UInt32 dwInfoContol,IntPtr _Deviceinfoptr, ref Int32 lBufSize);
說明:常見的數值類型,正常轉化;指針類型、引用類型直接轉化爲ref 數值類型。其中byte ** ,這種類型根據在c++中調用,是存儲數組的地址,因此在單純的用ref,不能解決問題。針對此問題,均可以用intptr,關鍵就是intptr怎麼指向,下面重點講講如何結構體怎麼訪問。函數
在函數調用時爲:佈局
iErr = INF_NET_GetList(g_lHandle, 5, (BYTE**)&m_pMonitorInfo, lMonSize);
其中m_pMonitorInfo是結構體數組,因此此函數是獲取一系列結構體,針對此,在c#函數中相對應聲明爲Intptr。ui
C#中看看如何利用Intptr,傳入地址,還有獲取數據。編碼
int iDev_size = Marshal.SizeOf(typeof(INFINOVASDK.INF_NET_DEVICE_INFO)); //定義一個存儲指針的數組 IntPtr[] _IntptrDevArray = new IntPtr[1]; //初始化指針數組大小 _IntptrDevArray[0] = Marshal.AllocHGlobal(iDev_size * 100);//分配包含100元素的數組 //查詢設備指針 IntPtr _IntptrDevInfo = Marshal.AllocHGlobal(iDev_size); //存儲設備指針於數組指針 Marshal.Copy(_IntptrDevArray, 0, _IntptrDevInfo, 1);//拷貝指針數組 int buffeesize = 0; int err = INFINOVASDK.INF_NET_GetList(_loginIds[control], 3, _IntptrDevInfo, ref buffeesize); if (err != 0) { return null; } //從指針數組開始獲取數據 for (int i = 0; i < 100; i++) { //設定好指針的偏移量 INFINOVASDK.INF_NET_DEVICE_INFO _deviceInfo = (INFINOVASDK.INF_NET_DEVICE_INFO)Marshal.PtrToStructure((IntPtr)((UInt32)_IntptrDevArray[0] + i * iDev_size), typeof(INFINOVASDK.INF_NET_DEVICE_INFO)); if (_deviceInfo.cDevIP != null) { //獲取設備的Ip以及MAC bytes_DevIp = _deviceInfo.cDevIP; bytes_Mac = _deviceInfo.cDevMac; break; } } //釋放申請的內存空間 Marshal.FreeHGlobal(_IntptrDevInfo); Marshal.FreeHGlobal(_IntptrDevArray[0]);
下面講講另外一個函數
c++函數spa
INF_NET_API INF_RESULT WINAPI INF_NET_FindFile (long & lFindHandle, INF_HANDLE lLoginHandle, LPINDEX_INFO pFindCondition,LPINDEX_INFO pRecvIndex);
上面函數中後兩個參數,類型都是結構體指針,可是在函數調用的時,不同。倒數第二個是傳入結構體的指針,指向一個結構體。倒數第一個指向結構體數組的,是 獲取的結構體數組。
指針
[DllImport(_strCoreSDK)] public static extern int INF_NET_FindFile(ref Int32 lFindHandle, Int32 lLoginHandle, IntPtr FIleptr, IntPtr RecordFileInfo);
接口函數,按照說明進行聲明。
C#函數調用,此方法和上面還不太相同。
//初始化查詢錄像文件對象
INFINOVASDK.NET_INDEX_INFO _FindFileInfo = new INFINOVASDK.NET_INDEX_INFO(); _FindFileInfo.dwStartTime = (uint)DatetimeToLong(startTime); _FindFileInfo.dwEndTime = (uint)DatetimeToLong(endTime); //查詢條件,所有文件 _FindFileInfo.btFileType = (byte)0xFF; //攝像機ID _FindFileInfo.dwIP4 = UInt32.Parse(cameraId); //平臺設備的Ip、以及MAc _FindFileInfo.dwIP1 = BitConverter.ToUInt32(bytes_DevIp, 0); _FindFileInfo.wChan = UInt16.Parse(devicechn); _FindFileInfo.btMAC = bytes_Mac; _FindFileInfo.dwReserved = 0; _FindFileInfo.Reserved = new byte[2];//此處應初始化,防止向指針賦值時出錯。 try { List<FileItem> files = new List<FileItem>(); //開始獲取錄像文件,並定義最多取100個文件 //開始設置查詢錄像文件的指針 int iIndexSize = Marshal.SizeOf(typeof(INFINOVASDK.NET_INDEX_INFO)); IntPtr _IntptrInedx = Marshal.AllocHGlobal(iIndexSize); Marshal.StructureToPtr(_FindFileInfo, _IntptrInedx, false);//在用Marshal類進行託管對象和非託管對象的轉換時,會有以下錯誤提示:「未能封送類型,由於嵌入數組實例的長度與佈局中聲明的長度不匹配。」因此針對結構體內數組要初始化,再賦值。看看賦值結構體內數組大小和初始化時結構體內數組大小是否相等。 //定義存儲錄像文件數組,並初始化 INFINOVASDK.NET_INDEX_INFO[] _ArrayFile = new INFINOVASDK.NET_INDEX_INFO[1024 * 10]; for (int i = 0; i < _ArrayFile.Length; i++) {//結構體初始化,防止出錯 _ArrayFile[i] = new INFINOVASDK.NET_INDEX_INFO(); _ArrayFile[i].Reserved = new byte[2];//針對數組專門初始化,關鍵是指明大小。 _ArrayFile[i].btMAC = new byte[6]; } //錄像文件數組定義個指針,並在內存空間申請的大小 IntPtr _IntptrRecordFiles = Marshal.AllocHGlobal(iIndexSize * 1024 * 10); //查詢錄像文件 int iFindhandle = 0; _errorCode = INFINOVASDK.INF_NET_FindFile(ref iFindhandle, _loginIds[control], _IntptrInedx, _IntptrRecordFiles); if (_errorCode != 0) { return null; } for (int i = 0; i < 10240; i++) { _ArrayFile[i] = (INFINOVASDK.NET_INDEX_INFO)Marshal.PtrToStructure((IntPtr)((UInt32)_IntptrRecordFiles + i * iIndexSize), typeof(INFINOVASDK.NET_INDEX_INFO)); if (_ArrayFile[i].dwStartTime != 0) { _NetRecordFiles.Add(_ArrayFile[i]); //獲取錄像文件的起止時間 DateTime tempstart = LongToDateTime(_ArrayFile[i].dwStartTime);//將datetime格式轉化爲int類型。 DateTime tempend = LongToDateTime(_ArrayFile[i].dwEndTime); string strstarttime = tempstart.ToString("yyyyMMddHHmmss"); string strendtime = tempend.ToString("yyyyMMddHHmmss"); FileItem _RecordFile = new FileItem(); _RecordFile.FileName = strstarttime + "_" + strendtime; _RecordFile.StartTime = tempstart; _RecordFile.EndTime = tempend; files.Add(_RecordFile); } } Marshal.FreeHGlobal(_IntptrInedx); Marshal.FreeHGlobal(_IntptrRecordFiles); return files; }
這個是以前複雜版本。
byte []_devipBytes = new byte [20]; byte []_macbytes = new byte [6]; //此前結構體內將char[]改成了string,結構看到數據是亂碼,因此須要進行數據轉化。 byte[] bytes = Encoding.UTF8.GetBytes(_deviceInfo.cDevIP); //_deviceInfo.cDevIP是string類型 //編碼轉化 byte[] tempdef = Encoding.Convert(Encoding.UTF8, Encoding.Default, bytes); //此處你會發現_devipBytes 原先大小爲20,此時因爲tempdef;爲4,而後此時_devipBytes 也爲4,這樣是不對的。 _devipBytes = tempdef; byte[] bytes1 = Encoding.UTF8.GetBytes(_deviceInfo.cDevMac); byte[] tempdef1 = Encoding.Convert(Encoding.UTF8, Encoding.Default, bytes1); //因此byte複製到byte,用此方法,這樣不會改便byte[]的大小 Array.Copy(tempdef1, _macbytes, 6);
若是想byte轉到string,能夠一個個獲取。
C# 默認的編碼方式是Unicode,而調用的DLL規定只處理UTF8編碼格式的字符串,DLL中的輸入參數類型char*被我Marshal成byte[],輸出參數類型char**被我Marshal成了string。用於接收時的string-->string(UTF8-->Unicode),會發現接受的數據string時爲亂碼,因此還要轉化爲char[],才能看到正確數據。用於發送時,還須要string-->byte[](Unicode-->UTF8)這樣頻繁的編碼轉換,看來編碼轉換並不是想象中那麼簡單:因此針對這種現場,當char * 做爲傳入數據時,直接處理爲char * 》》string。
(一)、Encoding和CharSet
在C#中包裝DLL的時候,DllImportAttribute當中的選項CharSet着:規定封送字符串應使用何種字符集,其中枚舉值有Ansi和Unicode,CharSet是字符集,用於字符傳送,Encoding表明編碼方式,用於數據轉化。字符集是字符的集合,規定這個集合裏有哪些字符,每一個字符都有一個整數編號(只是編號不是編碼);而編碼是用來規定字符編號如何與二進制交互,每一個「字符」分別用一個字節仍是多個字節存儲。CharSet代表在數據封送的字符集以什麼方式封送,而後在獲取到數據爲亂碼時,則經過Encoding裏面的函數,將數據轉化,則獲取到正確數據。
(二)、Ansi、Unicode、UTF八、bala bala
提到字符集,有ASCII、GB23十二、GBK、GB18030、BIG五、JIS等等多種,與此相對應的編碼方式爲ASCII、GB23十二、GBK、GB18030、BIG五、JIS,可是Unicode字符集卻有多種編碼方式:UTF-八、 UTF-七、UTF-1六、 UnicodeLittle、UnicodeBig,意味能夠講數據按照上述編碼方式進行轉化。 Ansi:系統編碼的發展經歷了三個階段,ASCIIàAnsi(不一樣國家語言本地化)àUnicode(標準化),原來Ansi編碼也好,Ansi字符集也好,都是指本地化的東西,在簡體中文系統下,ANSI 編碼表明 GB2312 編碼,Windows下自帶的記事本程序,默認的就是ANSI編碼。輸入「anhui合肥」(不含引號),保存編碼方式選「ANSI」,查看,哦,9個字節,明白了,原來Ansi編碼保留了對ASCII編碼的兼容,當遇到ASCII字符時,採用單字節存儲,當遇到非ASCII編碼時,採用雙字節表示(GB2312編碼)。
(三)、DllImportAttribute中的CharSet枚舉值選擇
對字符集和編碼的概念清楚了之後,終於能夠研究C#中調用非託管的DLL的方法咯。從託管應用程序去調用非託管代碼,若是CharSet=Unicode,則DLL中的接口函數將出現的全部字符串(包括參數和返回值)視爲Unicode字符集,Ansi同樣的道理
第一:我須要調用非託管代碼,第二:非託管代碼只處理UTF8編碼格式的字符,第三,萬惡的中文亂碼,接口函數簽名中,不管參數仍是返回值,接收到或是發送出的字符串都含有中文。首先接收字符串,由於接收到的是UTF8編碼格式,CharSet屬性確定要設置成Unicode了,接收後要正確顯示中文,在當前系統中,須要將編碼方式轉換成GB2312,即Encoding.Default;另外關於發送字符數組,由於不管是C#中默認的Unicode編碼方式,仍是DLL處理時規定的UTF8編碼方式,都是Unicode字符集的一種編碼方式,因此CharSet也要設置成Unicode,只是要在發送前須要將字符數組轉換一下編碼方式。如下附帶兩個簡單的函數實現。
// 轉換接收到的字符串
public string UTF8ToUnicode(string recvStr) { byte[] tempStr = Encoding.UTF8.GetBytes(recvNotify); byte[] tempDef = Encoding.Convert(Encoding.UTF8, Encoding.Default, tempStr); string msgBody = Encoding.Default.GetString(tempDef); return msgBody; } // 轉換要發送的字符數組 public byte[] UnicodeToUTF8(string sendStr) { string tempStr = Encoding.UTF8.GetString(sendStr); byte[] msgBody = Encoding.UTF8.GetBytes(tempUTF8); return msgBody; }
下面是經常使用的幾個函數
private DateTime LongToDateTime(uint times) { long _times =(long)times + 28800; DateTime ds = new DateTime(1970, 1, 1, 0, 0, 0,DateTimeKind.Unspecified).AddSeconds(_times); return ds; } private byte [] StructToBytes(Object structobj , int size) { byte []tempBytes = new byte [size]; IntPtr strcutIntptr = Marshal.AllocHGlobal(size); //將結構體拷貝到內存中 Marshal.StructureToPtr(structobj, strcutIntptr, false); //從內存空間拷貝到byte數組中 Marshal.Copy(strcutIntptr, tempBytes, 0, size); Marshal.FreeHGlobal(strcutIntptr); return tempBytes; } private object BytesToStruct(byte[] bytes ,Type _type) { int _size = Marshal.SizeOf(_type); IntPtr structInnptr = Marshal.AllocHGlobal(_size); Marshal.Copy(bytes, 0, structInnptr, _size); object obj = Marshal.PtrToStructure(structInnptr, _type); Marshal.FreeHGlobal(structInnptr); return obj; } private byte [] IntptrToBytes (IntPtr tempIntptr ,int _size) { byte[] tempBytes = new byte[_size]; //從內存空間拷貝到byte數組中 Marshal.Copy(tempIntptr, tempBytes, 0, _size); return tempBytes; } private IntPtr BytesToInptr(byte[] bytes, Type _type) { int _size = Marshal.SizeOf(_type); IntPtr structInnptr = Marshal.AllocHGlobal(_size); Marshal.Copy(bytes, 0, structInnptr, _size); return structInnptr; }