自己比较懒,有的时候想写点东西,但由于文笔不行、技术不行也就没有怎么写。经常是用到什么、学习什么的时候,简单写点,权当是个学习笔记。上博客的次数也很少,有人给我留言也是没有怎么及时的回复,深感抱歉!

在一些特殊的行业,比如我从事的GIS、地质行业,大部分软件还是以C/S形式存在,软件大多是产品来销售。这些程序大部分是Cpp语言来编写,一方面是考虑到效率问题,另一方面可能是因为历史原因,创建者使用Cpp,后面接班人也就继续使用。

但是使用Cpp去做项目的时候,又会倍感cpp的笨拙,做个界面非常费劲。所以如果能够使用C#语言来研发,使用WinForm、WPF来做界面,世界就会美好很多。可是软件产品生成的很多成果想要利用起来就比较困难,用C#重新写一遍系统是一条很好的路,技术难度低,但是工作量大,后期维护也比较困难,最主要的是在项目实施过程中,时间不够。另外一种思路就是对现有的Cpp系统进行包装,直接用C#调用,这几天比较了几种方法,最后使用CLR对C++进行封装了,可行性比较高。

一). PInvoke

不需要修改C++的DLL,直接在C#程序中把需要的接口引进进来即可。开始的时候感觉比较顺畅,但是后面越搞越麻烦,在CSharp和Cpp之间传递的个数组、传递个类,需要编写很多,并且MS上的文档也看得晕乎乎的。最后就放弃了。

1. 首先创建一个C++的普通DLL,从历史的DLL提取出自己想用的几个接口,暴露出来。(例子来自网上查询)

  1. #define SOFTWRAPPER_API extern "C" __declspec(dllexport)
  2.  
  3. // 简单的接口调用
  4. SOFTWRAPPER_API int fnCppDll(int a, int b);
  5. //带传入数组:
  6. SOFTWRAPPER_API void testArray1(const int N, const int n[], int& Z);
  7. //带传出数组:C++不能直接传出数组,只传出数组指针,
  8. SOFTWRAPPER_API void testArray2(const int M, const int n[], int *N) ;

2. 对应的实现

// 1. 把数据底层封装成一个全局函数,并导出
// 2. 编译好之后拷贝到CSharp运行路径

  1. SOFTWRAPPER_API int fnCppDll(int a, int b)
    {
  2. return a+b;
  3. }
  4. SOFTWRAPPER_API void testArray1(const int N, const int n[], int& Z)
  5. {
  6. for (int i=0; i<N; i++)
  7. {
  8. Z+=n[i];
  9. }
  10. }
  11. SOFTWRAPPER_API void testArray2(const int M, const int n[], int *N)
  12. {
  13. for (int i=0; i<M; i++)
  14. {
  15. N[i]=n[i]+10;
  16. }
  17. }

  编译成DLL之后,程序会把这几个接口暴露出去,可以使用depends.exe查看导出情况。

3. C#中对其调用

在C#中也建立一个类,专门用来管理这些接口

  1. namespace CSharp
  2. {
      // 3. 定义一个CSharp类,wrap c++的接口,方便CSharp使用
  3. class CppDll
  4. {
  5. [DllImport("CppDll.dll", CallingConvention = CallingConvention.Cdecl)]
  6. public static extern int fnCppDll(int x, int y);
  7.  
  8. [DllImport("CppDll.dll", EntryPoint = "#2", CallingConvention = CallingConvention.Cdecl)]
  9. public static extern double testArray(int N, int[] n, ref int Z);
  10.  
  11. [DllImport("CppDll.dll", EntryPoint = "#3", CallingConvention = CallingConvention.Cdecl)]
  12. public static extern void testOutArray(int N, int[] n, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] int[] Z);
  13. }
  14. }

这样定义之后在其他的C#程序中即可直接调用了。其中"CppDll.dll"是DLL文件名,保证在C#的输出目录下。声明的函数名称要么和CPP中的一致,要么不通过名字而通过EntryPoint = "#3"这种方式指定。确保编译器能找到接口

此种方法也可以实现类(C#中使用struct和IntPtr实现)的传输,但是需要在CSharp中重新编写类,并且变量的顺序、内存对齐方式可能都需要自己操心。尤其是我们类很多、类之间有嵌套等情况

MS网站有更详细的解释PInvoke和Marshal技术,可以根据这两个关键字在网络上好好查找下。

!!如何跟踪调试:在C#工程属性的Debug下面,check上Enable unmanaged code debugging即可。

二)SWIG

以前使用GDAL的时候,了解到GDAL可以在多中语言和多种平台下访问,底层使用C++编写,感觉很好。前几天也顺带看了下http://www.swig.org/

他的包装可以把类、接口很好的封装起来,但是使用SWIG封装的时候难度很大,要学习很多东西,网上有人评价,感觉是在学习一门新的语言,所以也没有进行一步深入下去。

三)D-BUS

本是LINUX等系统上的技术,“dbus的是一个低延迟,低开销,高可用性的ipc机制。是desktop-bus的简称”,因为我封装的DLL主要是数据服务,所以当时考虑这条技术路线,使用DBus提供服务器端的数据服务,然后C#做为客户端直接访问服务获取数据,感觉也是一条很好的路线。但是,感觉不是很正规,也放弃了。

关于SWIG和DBUS网上文档比较多,感兴趣的可以看看,多一种思路说不定什么时候可以用到:)

四)C++/CLI

在看Mashalling的时候,MS网站上到处都是托管代码这样的概念,于是深入了下,看了这个视频之后感觉这个很不错。

http://www.microsoft.com/uk/msdn/nuggets/nugget/184/Wrapping-Windows-APIs-with-CCLI.aspx

通过这视频了解了如何封装C++接口,同时也能看到高手是怎么编程的,受益匪浅

托管代码简单来说,就是在C++的基础上进行扩展,使得可以调用.Net里面的类库等东西。既然他是C++,那么他访问C++的DLL或者其他C++库,肯定是没有问题的了。另一方面,他支持.Net类库,那么就是说可以直接调用.Net里面的各种库了,同时提供了C++类型和.Net类型之间的各种转换。进一步,我们C#工程使用托管代码组成的DLL便能很方便的访问Native代码了。托管代码模块起到了桥梁的作用,连接了Native C++和CSharp

1. 新建CLR的类库:New Project下面选择VC++里面的CLR,下面的Class Libary。创建好之后,确保General属性页的Common Language Runtime Support 属性设置成了Common Language Runtime Support(/clr)

2. 编写类

  1. #pragma once
  2.  
  3. using namespace System;
  4. using namespace System::Data;
  5.  
  6. namespace CppSoftBridge {
  7. public ref class DataManger
  8. {
  9. public :
  10. bool Open(String^ path);
  11. property bool IsOpened{
  12. bool get(){return m_isOpened;}
  13. };
  14. property array<String^>^ AllData{
  15. array<String^>^ get();
  16. };
  17. }
  18. }

其中class前面的ref说明这个类是托管的,要在C#中调用。同样调用.Net类库的类也要有所区分,就是这里的符号 ^,相当于一个托管类的引用。

cpp中的实现

  1. using namespace System::Collections::Generic;
  2. using namespace System::Runtime::InteropServices;
  3. using namespace GPTSoftBridge;
  4.  
  5. array<String^>^ DataManger::AllData::get()
  6. {
  7. List<String^>^ list = gcnew List<String^>();
  8. // TODO, 这里可以访问Native C++里面的接口
  9. list->Add("1");
  10. list->Add("2");
  11. return list->ToArray();
  12. }
  13.  
  14. bool DataManger::Open(String^ path)
  15. {
  16. return true; // 根据自己需要进行实现即可
  17. }
  • 这里需要注意的是,这部分使用了.Net的东西,但是 还是C++的语法,
  • 比如导入库不是import,还是using语句。
  • 包的组织不是System.Collections.Generic而是System::Collections::Generic;
  • 申请对象托管代码需要使用gcnew,而不是new。
  • 申请之后如何判断是否为空呢?使用 if(p == nullptr)。
  • pin_ptr关键字能把托管引用转换为原生指针。   如: pin_ptr<BYTE> pBytes = & byteArray[0];

其余的.Net的类库就直接使用,C++的老代码也照常用就好了。

3. C#中使用

C#使用托管代码,和使用C#编写的类库方式一样。直接添加工程引用以后就可以直接使用了。

  1. DataManger dm = new DataManger();
  2. if (!dm.Open("D:\\datal"))
  3. return;
  4. String[] wells = dm.AllData;

就这么多了,通过上面的步骤,我们能很方便、快捷的把原来的DLL封装起来,供C#调用。以后再做项目,我们可以轻松的选择C#,并且可以同时使用原有的C++代码了。  

  

CSharp调用C++编写的DLL的方法的更多相关文章

  1. 使用clr 调用C#编写的dll中的方法的全解释

    使用clr 调用C#编写的dll中的方法的全解释1.数据库初始化:将下面这段代码直接在运行就可以初始化数据库了exec sp_configure 'show advanced options', '1 ...

  2. Delphi 调用C# 编写的DLL方法

    近来,因工作需要,必须解决Delphi写的主程序调用C#写的dll的问题.在网上一番搜索,又经过种种试验,最终证明有以下两种方法可行:    编写C#dll的方法都一样,首先在vs2005中创建一个“ ...

  3. C#动态调用C++编写的DLL函数

    C#动态调用C++编写的DLL函数 动态加载DLL需要使用Windows API函数:LoadLibrary.GetProcAddress以及FreeLibrary.我们可以使用DllImport在C ...

  4. C#调用C++编写的DLL函数, 以及各种类型的参数传递 (转载)

        C#调用C++编写的DLL函数, 以及各种类型的参数传递 1. 如果函数只有传入参数,比如: C/C++ Code Copy Code To Clipboard //C++中的输出函数 int ...

  5. 通过C#去调用C++编写的DLL

    这个问题缠了我2个小时才弄出来,其实很简单.当对方提供一个dll给你使用时,你需要去了解这个dll 是由什么语言写的,怎么编译的,看它的编译类型.这样即使在没有头绪时,你可以先尝使用一些比较热门的编译 ...

  6. C++项目中采用CLR的方式调用C#编写的dll

    1.注意事项:在编写C#DLL类库时,最好不要出现相同的命名空间,否则在C++中调用可能会出现编译错误.2.将C#的源码生成的“dll”文件复制到C++项目中的Debug目录下3.将C++项目属性设置 ...

  7. Delphi7调用DelphiXE编写的DLL问题

    http://bbs.csdn.net/topics/380045353 用DelphiXE在WIN2008下编写一个访问WebServices的DLL ws.dll,只有一个输出函数,如下: fun ...

  8. C#调用C++编写的dll

    界面还是C#写的方便点,主要是有一个可视化的编辑器,不想画太多的时间在界面上.但是自己又对C++了解的多一些,所以在需要一个良好的界面的情况下,使用C++来写代码逻辑,将其编译成一个dll,然后用C# ...

  9. C# 调用delphi编写的dll

    技术实现 如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,参考下面宏定义即可: #define LIBEXPORT_API extern "C" __declspe ...

随机推荐

  1. 9、搜索 :ion-searchbar

    /* ---html----*/ <ion-searchbar [(ngModel)]="searchQuery" (input)="getItems($event ...

  2. C#基础知识-引用类型和值类型的区别(六)

    在第一篇中我们介绍了C#中基本的15种数据类型,这15种数据类型中又分为两大类,一种是值类型,一种是引用类型.值类型有sbyte.short.long.int.byte.ushort.uint.ulo ...

  3. scss-!default默认变量

    在变量赋值之前, 利用!default为变量指定默认值. 也就是说,如果在此之前变量已经赋值,那就不使用默认值,如果没有赋值,则使用默认值. 代码实例如下: $content: "antzo ...

  4. js动画杂记

    在画布上做动画 方法有 setIntervel(function(){},time); setTimeout(function(){},time); 新方法 window.requestAnimati ...

  5. js变量定义提升、this指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级

    原文出自:http://www.cnblogs.com/xxcanghai/p/5189353.html作者:小小沧海 题目如下: function Foo() { getName = functio ...

  6. MySQL查询笔试综合题练习

    题目要求: 在某个数据库下建表: create table stu( -> name char(3) not null default '', -> subject varchar(10) ...

  7. GitHub网页端和客户端操作

    参见GitHub上的repository中的moreLove.tata.tata2 moreLove 在网页版GitHub上创建的空项目然后填充的tata 在windows客户端创建的空项目然后填充的 ...

  8. iOS中使用RNCryptor对资源文件加密

    原文:http://blog.csdn.net/chenpolu/article/details/46277587 RNCryptor源码https://github.com/RNCryptor/RN ...

  9. selenium 等待元素加载

    今天,尝试用代码指定自动化测试用例. 将测试record导出为C# 代码后,使用FF的drive ,发现执行一直失败,提示无法加载元素.顿时一种无力感袭来啊.还是硬着头皮找方法.尝试id name x ...

  10. mongodb 3.4 学习 (四)分片

    https://www.linode.com/docs/databases/mongodb/build-database-clusters-with-mongodb 由三部分组成 shard: 每个s ...