zoukankan      html  css  js  c++  java
  • C#与C++函数调用

    在 上篇文章重点讲解数据类型的转换,在此基础上再讲解函数调用。

    1、函数调用

    c++中函数

    INF_NET_API  INF_RESULT WINAPI INF_NET_GetList(long  lLoginHandle,DWORD dwInfoContol,BYTE**  pBuf,LONG&  lBufSize);

    C# 中函数

      [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。

    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++函数

    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、UTF8、bala bala
       提到字符集,有ASCII、GB2312、GBK、GB18030、BIG5、JIS等等多种,与此相对应的编码方式为ASCII、GB2312、GBK、GB18030、BIG5、JIS,但是Unicode字符集却有多种编码方式:UTF-8、 UTF-7、UTF-16、 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

       



  • 相关阅读:
    Data Base mysql备份与恢复
    java 乱码问题解决方案
    【知识强化】第二章 物理层 2.1 通信基础
    【知识强化】第二章 进程管理 2.2 处理机调度
    【知识强化】第二章 进程管理 2.1 进程与线程
    【知识强化】第一章 操作系统概述 1.3 操作系统的运行环境
    【知识强化】第一章 网络体系结构 1.1 数据结构的基本概念
    【知识强化】第一章 网络体系结构 1.2 计算机网络体系结构与参考模型
    【知识强化】第一章 网络体系结构 1.1 计算机网络概述
    【知识强化】第一章 操作系统概述 1.1 操作系统的基本概念
  • 原文地址:https://www.cnblogs.com/polly333/p/4498401.html
Copyright © 2011-2022 走看看