C#與C++函數調用

在 上篇文章重點講解數據類型的轉換,在此基礎上再講解函數調用。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]);
View Code

 

下面講講另外一個函數
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;
            }
View Code

 

這個是以前複雜版本。

 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;
}  
View Code

 

    下面是經常使用的幾個函數

 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;
        }
View Code
相關文章
相關標籤/搜索