转向.NET后,手头上往往仍有旧的模块要重用。也许这些模块是Delphi写的,也许是C/C++写的,或者是其它编程语言……为了能把它们移植到.NET下,或者是在.NET中调用,To be or not to be, that is a question。
在这里,我笔记了几个在工作中遇到的几个场景。不过,这里不包括完全使用C#来重写原来用C++编写的程序这种变态的需求。当你被要求做这种事的时候,请三思而后行……这简直是种非人的折磨。
场景一:在.NET中调用WindowsAPI或DLL。
这是比较普遍的需求。一般来说,简单的函数调用,大可直接用C#/VB.NET,经过DllImport属性包装出函数来调用。如:

[DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.StdCall)]
public static extern bool MoveFile(String src, String dst);

由于WindowsAPI用到的人实在是多,因此有一个专门的wiki站点,收集这方面的资料:http://www.pinvoke.net/,对于常用的函数甚至有完整的应用例子和帮助。当然,如果你有相应的资料和例子,你也可以贡献你的力量,给其它人帮助。
场景二:用托管C++包装现有的DLL,供C#调用
当函数的参数或返回值比较复杂,或函数比较多的时候,这种方法对与人来说,实在是一个折磨。常常这些接口和定义就要用掉几千行的代码,而且还不能保证是正确的。这些错误往往在运行时才能显现出来,甚至有些错误会引起内存泄漏,或其它更为隐蔽的错误。
在这种情况下,使用C++/Managed代码来包装,就成了最合理的选择。因为托管C++代码可以直接引用原有的头文件,直接调用非托管函数,而不需要声明。这样,既减少了工作量,又避免引入错误。缺点是,这种方法会增加一个DLL。要注意的是托管字符串和非托管字符串是有区别的,并需要转换(特别要注意的Unicode字符串和多字节字符串的转换)。
仍以MoveFile为例吧,这样比较简单:

#include <windows.h>
#include <vcclr.h>

using namespace System;

namespace wrapper
{
public ref class ApiWrapper {
public:
bool static MoveFile(String ^ lpExistingFileName, String ^ lpNewFileName )
{
pin_ptr<const wchar_t> src = PtrToStringChars(lpExistingFileName);
pin_ptr<const wchar_t> dst = PtrToStringChars(lpNewFileName);
return ::MoveFile(src, dst);
}
};
}

然后在C#中,引用上面代码生成的DLL文件,就可以直接调用了:

wrapper.ApiWrapper.MoveFile(@"c:\debug.log", @"c:\debug.txt");

假如原有的代码是基于COM的,那么太好了,VisualStudio等IDE会自动生成一个用于包装的dll,供你调用。当然因特殊需要而手工编码的是另一回事。
场景三:现有C++原代码,包装后供C#调用。
C++的原代码,实际上可以直接编译成托管代码。MFC也好ATL也好……这样看起来在.NET中最强大的编程语言就是C++了:它不仅可以编写托管程序,甚至可以将标准C++的代码也编译成托管程序!其实VC++最强大的地方不止如此,它还在于能够编写混合了托管和非托管的代码的程序!!!这样最大的好处不仅可以将关键代码直接编译成非托管的代码,还可以避免被反编译。
假设现有C++代码如下:

class UnmanagedClass{
public: 
LPCWSTR GetPropertyA(){ return L"Hello!"; } 
void MethodB( LPCWSTR ){}
};

我们只要再增加一个包装类到工程文件中:

namespace wrapper

public ref class ManagedClass { 
public: 
// Allocate the native object on the C++ Heap via a constructor 
ManagedClass() : m_Impl( new UnmanagedClass ){} 
 
// Deallocate the native object on a destructor 
~ManagedClass() { 
delete m_Impl; 

 
protected: 
// Deallocate the native object on the finalizer just in case no destructor is called 
!ManagedClass() { 
delete m_Impl; 

 
public: 
property String ^ get_PropertyA { 
String ^ get() { 
return gcnew String( m_Impl->GetPropertyA()); 


 
void MethodB( String ^ theString ) { 
pin_ptr<const WCHAR> str = PtrToStringChars(theString); 
m_Impl->MethodB(str); 

 
private: 
UnmanagedClass * m_Impl; 
};
}

然后,改变编译选项为“使用公共语言扩展 /clr”就可以了。这样,我们把代码编译成DLL文件就可以供.NET其它语言调用了。
最后,C#中可以象如下的代码一样调用C++类了:

ManagedClass mc = new ManagedClass();
mc.MethoB("Hello");
string s = mc.get_PropertyA;

场景四:如何在托管C++代码中混合托管和非托管代码
很简单,只要从#pragma unmanaged编译指示开始的程序,一率编译成非托管代码;要想恢复成托管代码,只要使用#pragma managed就可以了。如:

#pragma unmanaged

#include <iostream>
using namespace std;

template<typename T>
void f(T t){ 
cout << t << endl;
}

#pragma managed

using namespace System;
 
void m(String ^ s){ 
Console::WriteLine(s);
}
 
void main(){ 
f("Hello"); 
m("World");
}

生成exe文件后,用反编译程序查看 f 函数:

[PreserveSig, MethodImpl(MethodImplOptions.Unmanaged, MethodCodeType=MethodCodeType.Native), SuppressUnmanagedCodeSecurity]
public static unsafe void modopt(CallConvCdecl) f<char const *>(sbyte modopt(IsSignUnspecifiedByte) modopt(IsConst)*);

看不到源码,而方法属性标记为Unmanaged。
如果没有加上#pragma unmanaged,反编译得到的 f 函数为:

internal static unsafe void modopt(CallConvCdecl) f<char const *>(sbyte modopt(IsSignUnspecifiedByte) modopt(IsConst)* t)

std.basic_ostream<char,std::char_traits<char> >.<<(std.operator<<<struct std::char_traits<char> >(*((basic_ostream<char,std::char_traits<char> >* modopt(IsImplicitlyDereferenced)*) &__imp_std.cout), t), (basic_ostream<char,std::char_traits<char> >* modopt(IsImplicitlyDereferenced) modopt(CallConvCdecl) *(basic_ostream<char,std::char_traits<char> >* modopt(IsImplicitlyDereferenced))) __unep@?endl@std@@$$FYAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z);
}

其中的函数内容一目了然。如果你的函数没有调用operator等不好理解的类库,那么反编译出来的代码简直和源码没差别。
开心一刻:我只会C++不懂.NET不懂C#,怎么编写.NET程序?
很简单,你照样用你的C++写你的程序,然后测试没有错误后,将编译选项改为/clr,好了,Rebuild,你的程序现在是.NET了。
恶搞:“我想问一下,在能将现有的C++代码直接进行封装,被C#进行调用,而不是去调用DLL,也就是不生成DLL,就在C#下能直接调用VC的工程源文件不?”
我想,提问的人是不是指,现有c++源码,但不想费劲去转换成C#源码,但又想能与C#一起编译。
于是我就给了一个极其变态的方法,不过,个人是不建议使用这种变态的方法啊。方法如下:
1 先将C++源码,改用CLR编译选项,编译成.NET的Assembly(DLL文件)。
2 然后用reflector等反编译软件,反编译成C#代码,并导出(reflector有专门的导出插件)。
3 将导出的C#代码,添加上新写的C#代码一起编译。
这种方法生成的代码很是恐怖,强烈建议不要把C++源码就这么丢了,否则后果自负。

转载于:https://www.cnblogs.com/swordzj/archive/2011/09/05/2168023.html

转帖:由C++转向C#的几种对策相关推荐

  1. 王厚祥谈《古诗四帖》基本笔画的书写方法

    狂草的笔画与其他书体不同,它的起.行.收不是以一个笔画为单位的,而是以一个书写节奏为单位.<古诗四帖>作为狂草作品,连绵书写是其最为明显的特征.连绵书写产生的结果是,一条线可能写一个字,也 ...

  2. 死宅福音:乐高不怕多,智能分拣机帮你归类

    作者 | 神经小兮 来源 |  HyperAI超神经(ID:HyperAI) [导读]乐高现在几乎已经是优质玩具的代名词,该品牌旗下最为知名的,莫过于乐高积木.其丰富的形状与多样的玩法,无论大人小孩都 ...

  3. 创新工场南京人工智能研究院执行院长冯霁:联邦学习中的安全问题

    近期,创新工场南京人工智能研究院执行院长冯霁做客雷锋网AI金融评论公开课,以"浅析联邦学习中的安全性问题"为题,详尽地讲解了联邦学习的特点.联邦学习的应用和安全防御对策等内容. 以 ...

  4. 神经网络支持大脑是「预测机器」,预测是节能的

    来源:ScienceAI 编译:绿萝 我们的大脑,一个包裹在骨性头骨内的三磅重的组织,如何从感觉中产生知觉是一个长期存在的谜.大量证据和数十年的持续研究表明,大脑不能像拼拼图一样,简单地组合感官信息来 ...

  5. 艾伟_转载:C# Design Patterns (3) - Decorator

    Decorator Pattern (装饰模式) 装饰模式可「动态」地给一个对象添加一些额外的职责,提供有别于「继承」的另一种选择.就扩展功能而言,Decorator Pattern 透过 Aggre ...

  6. 现浇板弹性计算还是塑性计算,板塑性和弹性

    现浇板弹性算法和塑性算法的区别是什么?在何种情况下采用刚性楼板假定? 最佳答案 现浇板弹性理论就是假设楼板变形是弹性,而塑性理论则假设楼板变形为塑性,通常是指带裂缝工作,由于楼板带裂缝工作的,此时楼板 ...

  7. 2018-01-02 JavaScript实现ZLOGO: 用语法树实现多层循环...

    照例先上演示弱效果图. 演示地址照旧: 代码如下: 开始循环4次循环4次前进50左转90度到此为止右转90度到此为止 结束 如上文JavaScript实现ZLOGO子集: 测试用例末尾所言, 此文用A ...

  8. 一篇文章带你详解 HTTP 协议之报文首部及字段详解(中)

    在上一篇中,我们已经讲解了一到五的内容,还没看过的可以先看, 下面接着讲剩余内容: 六.HTTP 报文首部之首部字段(重点分析) 七.HTTP 响应状态码(重点分析) 八.HTTP 报文实体 九.与 ...

  9. Kudu:为大数据快速分析量身定制的 Hadoop 存储系统

    Apache Hadoop提供了一系列数据存储与处理的组件,覆盖了多种多样.应用于企业级关键服务的用户案例.在Cloudera,我们一直在努力探索Hadoop的各种可能性,拓展Hadoop的边界--使 ...

最新文章

  1. hbase异步客户端连接-非阻塞并发模式实验记录(还没弄完)
  2. java用接口实例化对象_[求助]迷茫中,接口可以直接实例化对象吗?
  3. OPENQUERY用法以及使用需要注意的地方
  4. XmlHttpRequest错误:Access-Control-Allow-Origin不允许使用原点null
  5. 每个项目都不同:支持一个摄像头,到现在还没搞好
  6. 没有wan接口_“伪千兆”路由器,快回家看看你家中招了没有?
  7. Eclipse/MyEclipse一次修改或者编辑多行的快捷键
  8. 全流程各工程类型地下水环境影响评价【一级】方法与MODFLOW Flex建模技术
  9. Django报错异常django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without
  10. 笔记本电脑不要锁定计算机,笔记本电脑键盘怎么解锁呢
  11. 深度学习(Deep Learning)综述及其延伸,入门必看!
  12. 【周博磊】强化学习纲要 一至六讲笔记
  13. 华为推出打车平台 Petal,科技大厂再战聚合打车
  14. plsr matlab,matlab中的偏最小二乘回归(PLSR)和主成分回归(PCR)
  15. 社交网络中基于张量分解的好友推荐
  16. 二极管质量好坏判断方法,轻松学会-通程创品
  17. [Linux Audio Driver] SM6350平台音频bring up ( 一 )
  18. 计算机对英语写作的帮助,计算机文字处理与英语写作教学的融合
  19. Docker实践总结
  20. 2019湖南多校3 What Goes Up Must Come Down (树状数组)

热门文章

  1. 复制构造函数与析构函数
  2. opencv Scharr、Canny、LOG边缘提取效果对比
  3. C++ STL 程序员必须会之学会删除容器中的元素 带详细注释实例版本
  4. [高光谱] Hyperspectral-Classification Pytorch 的高光谱场景的通用类 HyperX
  5. Docker Swarm 使用
  6. Original error was: DLL load failed while importing _multiarray_umath: 找不到指定的模块
  7. 原生JS 将canvas生成图片
  8. [Leetcode 18]四数之和 4 Sum
  9. weblogic发序列化命令执行漏洞工具分享
  10. mysql分组查询n条记录