zoukankan      html  css  js  c++  java
  • [转贴]实践:C++平台迁移以及如何用C#做C++包装层

    终于有个C++ 如何调用C#类库的文章,收藏之

       在前面,我们看过OpenTK与MOgre,这二个项目都是C#项目,但是他的实现都是C++.他们简单来说就是一个包装层.常见的包装方式有二种,一 种就是我们熟知的显式P/Invoke(DllImport),上面所说的OpenTK就是这种,还有一种就是C++ -> C++/CRL -> C#,这种也叫隐式P/Invoke,也有称C++ Interop,这篇文章主要讲的就是隐式P/Invoke,具体相关操作请见http://msdn.microsoft.com/zh-cn/library/2x8kf7zx.aspx .

         先说下这个项目的需求,有二个C++项目,都是和信号收集设备打交通,一个是在window环境下实现了设备的实时模式,另外一个项目是在IOS环境下 写的设备的黑盒模式.现在要求就是把IOS环境下写的代码整合到第一个项目window环境下,然后再提供C#层面的包装接口与一个简单运行的DEOM. 需求很简单,主要是我从离开大学一直玩的就是C#,C++好久没用过,如果以下说的有错误的地方,欢迎大家指出,我会马上修改.同时也谢谢公司给我的这次 实践,从开始自学游戏开发(OpenGL为主)开始,面对全是C++代码,虽然读与翻译成别的类型语言都能做到,就是一直没机会系统的用上C++,这次算 是对C++项目的初级改动.
         先说下我的开发环境,用的是VS2013,如下问题有些版本可能不一样.
         从IOS环境下的代码,移到vs2013中,首先是如sprintf这种都会报错,因为vs2013会默认启用安全处理,sprintf的安全版本是 sprintf_s,会加上缓冲区检查,还有一些如strcpy,strcpy_s,localtime,localtime_s.一般提示你启用安全版 本,在对应的函数后添加_s,参数个数可能也会变,不过根据定义都容易改.
         然后就是socket里的一些原型参数的区别了,在IOS或是LINUX里,如关闭socket直接是close,在vs2013中是 closesocket,ioctl与ioctlsocket也是如此,如果编译出错,又知道是socket里的函数,可以尝试在原函数后加上 socket,然后是查询广播这个地方二个位置的写法差别比较大,在原IOS环境下ioctl传入SIOCGIFCONF命令,得到对应的ifconf数 组,然后把ifconf数组每个值通过ioctl传入SIOCGIFBRDADDR命令得到对应的广播地址,我开始尝试把其中的一些ifconf 结构引用进来,后来发现关联的比较多,现在想了一想,就是全添加进来,应该也是不行的.而在window环境下,直接使用WSAIoctl,传入命令 SIO_GET_INTERFACE_LIST得到INTERFACE_INFO的结构体,里面就包含了所需要的广播地址,别的区别就不大了,相关的很多 API是通用的.
         到这里,差不多原来的IOS环境下的代码就转到VS2013中了,然后就是把这部分代码整合到原来的实时模式下,因为二个项目有些头文件本来就有重合 的,或者重合的结构体,但是有些属性名不一样,二个相同功能的类,但是部分API与成员不一样的问题,在这我的办法是先把原IOS环境下的代码大致看一 下,确定引用关系前后,就是没引用项目内别的文件与引用项目内别的文件最多的文件的.h与.cpp文件,先把引用关系最小的.h与点.cpp文件转移过 去,然后确保能正确编译,然后再按顺序,一个个移过来,对于高手不清楚,但是对于我们这种小白,能保证不会因大堆错误而烦心,每转移一个文件成功后都会感 觉到高兴.
         在这,实时模式和IOS下的黑盒模式代码都成功整合到VS2013中了,这里遇到一个问题.如下代码 所示.
         std::thread detectThread(&PApi::DetectProc, this);
         detectThread.detach();
         在原IOS环境下用的线程都是用的STL下的线程类,在主线程下,初始化一个类中包含如上 STL线程类的初始化,会卡在初始化那里,根本进入不了detach这个方法,最开始是单独重建一个函数,包含上面的代码,在初始化后调用这个函数没有问 题.但是这个功能本来应该是在初始化就启用的,再初始化后再加一函数,感觉不好,故把相关线程改成window api里的线程启用.问题解决,代码如下,需要把成员函数DetectProc改成静态成员函数.  
         DWORD threadId = 0;
         HANDLE threadHandle = CreateThread(0, 0, DetectProc, this, 0, &threadId);
         CloseHandle(threadHandle);
         threadHandle = INVALID_HANDLE_VALUE;  
         这是一个小插曲,问题解决后,我们按要求转移到C#平台.如最前面所说,采用的是隐式P/Invoke,我们来看看是如何实现的.
         我们用C++实现全局钩子的同学们都知道,非托管C++DLL里的函数不同C#托管DLL,你在里面写个Public,引用这个DLL的项目就能看到,非托管C++动态链接库想让外面知道内部的API,需要添加关键字__declspec(dllexport).而不管是显示P/Invoke还是隐式P/Invoke引用非托管C++里的API,都需要相关API声明成__declspec(dllexport).
         在这里,说下P/Invoke显示调用与隐式调用的用法的不同,显示调用一般是在C#中使用DLLImport特性包装非托管语言用关键字__declspec(dllexport) 公开的API.而隐式调用一般会新建一个托管C++项目,也就是C++/CRL,托管C++和非托管C++共同点是.h头文件.cpp源文件分类与头文件 引用这些,别的部分可能让我感觉更像是C#,通过托管C++生成的动态链接库就和我们C#生成DLL一样,直接能被托管项目引用调用类啥的,而托管C++ 也能像非托管C++一样,引用头文件直接调用相应API,不需要用DLLImport,但其实总的来说更麻烦些,不过能实现的功能也多些,有些C++好用的方法在C#里没有,如memcyp,一些针对指针的操作也和C++一样方便.
         __declspec(dllexport)是我们暴露给外部的DLL生成API所需的,对应我们用P/Invoke引用这些公开的API时,最好在对应的方法上给出关键字__declspec(dllimport),虽然导出函数不是必要的,但是如果是DLL中的变量,这个是必需的,并且编译器也能针对关键字做特定优化.下面是一段常见代码.     
    View Code
       如上定义一个宏定义,在导出的C++动态链接库中,可以选择项目属性里添加预处理器定义TRADITIONALDLL_EXPORTS,也或者是在引用这个文件加上.而在引用这个动态链接库不做处理.
         先看一个普通的函数从非托管C++到C++/CRL到C#相应流程,这个函数是传入一个设备ID,得到设备的所有工程,以及默认的工程ID.
    View Code

         基本的传递如上,但是现在要求C#实时刷新设备转过来的数据,简单来说,就是C++里socket接收线程收到设备发送的数据,需要通知C#界面刷新. 看需求,C#里的事件就能满足,但是是C++发送的消息,在这我们根据C++里的回调函数与托管代码里的事件结合来完成,去掉一些不必要的代码,主要过程 如下.

    View Code
      C++里的memcyp确实很好用,上段代码中,ByteArrayToStructure也能实现如memcyp一样的功能,先用GCHandle.Alloc选择Pinned生成CG不能回改的内存区域,就和C++申请内存一样,然后根据偏移量offset,把对应的字节转成我们需要的数据.C++里的char和C#里的byte是一样的,都是一个字节,这里不要搞错了,也和C++一样,记的清除申请的内存空间.
  • 相关阅读:
    < java.util >-- Set接口
    Codeforces 627 A. XOR Equation (数学)
    Codeforces 161 B. Discounts (贪心)
    Codeforces 161 D. Distance in Tree (树dp)
    HDU 5534 Partial Tree (完全背包变形)
    HDU 5927 Auxiliary Set (dfs)
    Codeforces 27E. Number With The Given Amount Of Divisors (暴力)
    lght oj 1257
    Codeforces 219D. Choosing Capital for Treeland (树dp)
    Codeforces 479E. Riding in a Lift (dp + 前缀和优化)
  • 原文地址:https://www.cnblogs.com/redmondfan/p/3748502.html
Copyright © 2011-2022 走看看