[转贴]实践: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中的变量,这个是必需的,并且编译器也能针对关键字做特定优化.下面是一段常见代码.



   如上定义一个宏定义,在导出的C++动态链接库中,可以选择项目属性里添加预处理器定义TRADITIONALDLL_EXPORTS,也或者是在引用这个文件加上.而在引用这个动态链接库不做处理.

先看一个普通的函数从非托管C++到C++/CRL到C#相应流程,这个函数是传入一个设备ID,得到设备的所有工程,以及默认的工程ID.



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



  C++里的memcyp确实很好用,上段代码中,ByteArrayToStructure也能实现如memcyp一样的功能,先用GCHandle.Alloc选择Pinned生成CG不能回改的内存区域,就和C++申请内存一样,然后根据偏移量offset,把对应的字节转成我们需要的数据.C++里的char和C#里的byte是一样的,都是一个字节,这里不要搞错了,也和C++一样,记的清除申请的内存空间.

时间: 2024-10-08 21:21:13

[转贴]实践:C++平台迁移以及如何用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/2x8kf7

PVS vdisk从XenServer平台迁移到ESXi平台经验分享

跨平台镜像转换,从XS平台vdisk迁移到ESXi,怎么保障母镜像从原有平台迁移到新的平台,启动VDI不会蓝屏或黑屏 以下是操作步骤: 第一部分平台迁移过程,比较多,请注意! 1.在XS平台,将VDI的vdisk,转成VM 2.在VM卸载Cirix组件但XStools先不能卸载(否则平台VPV无法完成),卸载杀毒软件,重启后安装 VMware-converter,将VM迁到ESXI平台 3.注意在ESXI平台VM"VDI"无法开机,需新建空壳虚拟机: 4.通过挂载VM"VDI

将程序从windows平台迁移到Linux平台

Windows生产环境: OS: Windows 2003 Web服务器:Apache Tomcat/6.0.26 Java虚拟(JVM):1.6.0_10-rc2-b32 程序语言:Java+JSP Linux生产环境: OS: CentOS release 6.7 (Final) Web服务器:Apache Tomcat/7.0.57 Java虚拟(JVM):1.7.0_71-b14 程序语言:Java+JSP 任务:将从Windows平台上的成绩上传监控程序迁移到Linux平台上. 操作务

核心业务系统数据库平台迁移: Oracle -> MySQL

为了对核心技术拥有更多的自主控制能力,为了解决数据库的线性扩展问题,为了尽量减少对商业软件的依赖,为了摆脱对高端硬件的依赖,为了… 基于以上多种原因,2年前,我们计划将公司某核心应用平台进行大手术:数据库平台从软件到硬件全部重构.当然,这其中应用架构的改造也不可避免的进行了大换血. 这个项目无论是从技术角度还是是业务角度来说,都对我们有着非常大的价值,也必定会带来非常深远的影响.项目历时2年多,分4个阶段才完成: 应用接口统一 这一阶段主要是为了后面真正迁移的时候做准备工作,将该核心应用系统的所

使用GoldenGate进行平台迁移和数据库升级(9i->11g)步骤描述

在一个场景中,需要从Solaris SPARC将数据库迁移到Linux X86-64,同时,数据库版本从原有的oracle 9i(9.2.0.5)升级到11g(11.2.0.4)使用OGG的数据同步功能,停机时间的长短主要取决于旧应用断开和新应用对外提供访问之间的切换时间.升级步骤如下: 1. 目标端机器上安装Oracle 11.2.0.4软件,并根据源端的DB设置配置相应的表空间和数据库参数,需要注意的是9i中有些参数在11g中已经废弃或变化:部署应用到新的平台2. 在源和目标端服务器上安装O

Android c/c++ 应用向linux 平台迁移执行

鉴于近期在minicamera (linux + FPGA) 平台上模拟 安卓系统camera流程遭遇不断升级同步的困扰.尤其是 不开放源代码后 , 应用要链接封闭动态库造成的困难.以及在sprdisk上要不断升级对应的minicamera.minicamera 不能随着安卓系统同步升级. 这两个互相交杂在一起的致命问题,严重迟缓了camera 应用和内核驱动的开发測试进度. 在深圳同事的启示下,做最简单的方案尝试 1 将安卓的 c/c++ 应用及其依赖so编译出来. cp到 buildroot

KVM虚拟平台——迁移KVM虚拟机

博文大纲:一.KVM虚拟机迁移的方式二.实现KVM虚拟机静态迁移三.实现KVM虚拟机动态迁移 一.KVM虚拟机迁移的方式 KVM平台中的KVM虚拟机迁移分为以下两种: (1)冷迁移(静态迁移) 我们存放虚拟机磁盘的目录都是挂在的一个nfs文件系统的磁盘,进行冷迁移时,只要在目标主机上挂载这个nfs文件系统,就可以看到要迁移的那个虚拟机的磁盘文件,通常以.qcow2或.raw结尾的,然后,只需将虚拟机的.xml配置文件发送到目标服务器上,然后重新定义一下迁移过来的虚拟机即可! (2)热迁移(动态迁

Android c/c++ 应用向linux 平台迁移运行

最简单的方案尝试 1 将安卓的 c/c++ 应用及其依赖so编译出来, cp到 buildroot  生成的target 目录下的 /bin 和/lib 下 2 将安卓的 linker 这个运行解释器cp 到target 目录下的 /bin http://www.linuxidc.com/Linux/2014-03/97499.htm http://blog.csdn.net/dinuliang/article/details/5509009 http://www.cnblogs.com/sdp

DB2同平台迁移数据库

[[email protected] bakdata]$ db2 backup db EATH online to /tmp/bakdata/ include logs without prompting Backup successful. The timestamp for this backup image is : 20190611173438 db2pd -db EATH -tab Address TspId ContainNum Type TotalPgs UseablePgs Pa