转向.NET后,手头上往往仍有旧的模块要重用。也许这些模块是Delphi写的,也许是C/C++写的,或者是其它编程语言……为了能把它们移植到.NET下,或者是在.NET中调用,To be or not to be, that is a question。

  在这里,我笔记了几个在工作中遇到的几个场景。不过,这里不包括完全使用C#来重写原来用C++编写的程序这种变态的需求。当你被要求做这种事的时候,请三思而后行……这简直是种非人的折磨。


​您也使用托管C++吗?​​  ​​如沐枫林​


  场景一:在.NET中调用WindowsAPI或DLL。


  这是比较普遍的需求。一般来说,简单的函数调用,大可直接用C#/VB.NET,经过DllImport属性包装出函数来调用。如:

="MoveFileW",  SetLastError=true,

您也使用托管C++吗? 【转】_#pragmaCharSet=CharSet.Unicode, ExactSpelling=true,

您也使用托管C++吗? 【转】_.net_02CallingConvention=CallingConvention.StdCall)]

您也使用托管C++吗? 【转】_反编译_03public static extern bool MoveFile(String src, String dst);您也使用托管C++吗? 【转】_.net_04

  由于WindowsAPI用到的人实在是多,因此有一个专门的wiki站点,收集这方面的资料:​​http://www.pinvoke.net/​​​,对于常用的函数甚至有完整的应用例子和帮助。当然,如果你有相应的资料和例子,你也可以贡献你的力量,给其它人帮助。


   场景二:用托管C++包装现有的DLL,供C#调用


  当函数的参数或返回值比较复杂,或函数比较多的时候,这种方法对与人来说,实在是一个折磨。常常这些接口和定义就要用掉几千行的代码,而且还不能保证是正确的。这些错误往往在运行时才能显现出来,甚至有些错误会引起内存泄漏,或其它更为隐蔽的错误。

  在这种情况下,使用C++/Managed代码来包装,就成了最合理的选择。因为托管C++代码可以直接引用原有的头文件,直接调用非托管函数,而不需要声明。这样,既减少了工作量,又避免引入错误。缺点是,这种方法会增加一个DLL。要注意的是托管字符串和非托管字符串是有区别的,并需要转换(特别要注意的Unicode字符串和多字节字符串的转换)。


  仍以MoveFile为例吧,这样比较简单:

<windows.h>

您也使用托管C++吗? 【转】_c++_05#include <vcclr.h>

您也使用托管C++吗? 【转】_c++_06

您也使用托管C++吗? 【转】_.net_07using namespace System;

您也使用托管C++吗? 【转】_c#_08

您也使用托管C++吗? 【转】_c#_09namespace wrapper

您也使用托管C++吗? 【转】_c#_10{

您也使用托管C++吗? 【转】_c++_11    public ref class ApiWrapper {

您也使用托管C++吗? 【转】_反编译_12    public:

您也使用托管C++吗? 【转】_c++_13        bool static MoveFile(String ^ lpExistingFileName, String ^ lpNewFileName )

您也使用托管C++吗? 【转】_c++_14        {

您也使用托管C++吗? 【转】_.net_15            pin_ptr<const wchar_t> src = PtrToStringChars(lpExistingFileName);

您也使用托管C++吗? 【转】_#pragma_16            pin_ptr<const wchar_t> dst = PtrToStringChars(lpNewFileName);

您也使用托管C++吗? 【转】_.net_17            return ::MoveFile(src, dst);

您也使用托管C++吗? 【转】_c#_18        }

您也使用托管C++吗? 【转】_c++_19    };

您也使用托管C++吗? 【转】_.net_20}


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

@"c:/debug.txt");


  假如原有的代码是基于COM的,那么太好了,VisualStudio等IDE会自动生成一个用于包装的dll,供你调用。当然因特殊需要而手工编码的是另一回事。


  场景三:现有C++原代码,包装后供C#调用。


  C++的原代码,实际上可以直接编译成托管代码。MFC也好ATL也好……这样看起来在.NET中最强大的编程语言就是C++了:它不仅可以编写托管程 序,甚至可以将标准C++的代码也编译成托管程序!其实VC++最强大的地方不止如此,它还在于能够编写混合了托管和非托管的代码的程序!!!这样最大的 好处不仅可以将关键代码直接编译成非托管的代码,还可以避免被反编译。


  假设现有C++代码如下:

{

您也使用托管C++吗? 【转】_c++_21public:

您也使用托管C++吗? 【转】_反编译_22    LPCWSTR GetPropertyA() { return L"Hello!"; }

您也使用托管C++吗? 【转】_c++_23    void MethodB( LPCWSTR ) {}

您也使用托管C++吗? 【转】_.net_24};

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

{

您也使用托管C++吗? 【转】_c#_25    public ref class ManagedClass {

您也使用托管C++吗? 【转】_c++_26    public:

您也使用托管C++吗? 【转】_.net_27        // Allocate the native object on the C++ Heap via a constructor

您也使用托管C++吗? 【转】_c#_28        ManagedClass() : m_Impl( new UnmanagedClass ) {}

您也使用托管C++吗? 【转】_c++_29

您也使用托管C++吗? 【转】_反编译_30        // Deallocate the native object on a destructor

您也使用托管C++吗? 【转】_c++_31        ~ManagedClass() {

您也使用托管C++吗? 【转】_c++_32            delete m_Impl;

您也使用托管C++吗? 【转】_c++_33        }

您也使用托管C++吗? 【转】_反编译_34

您也使用托管C++吗? 【转】_#pragma_35    protected:

您也使用托管C++吗? 【转】_c#_36        // Deallocate the native object on the finalizer just in case no destructor is called

您也使用托管C++吗? 【转】_反编译_37        !ManagedClass() {

您也使用托管C++吗? 【转】_c#_38            delete m_Impl;

您也使用托管C++吗? 【转】_.net_39        }

您也使用托管C++吗? 【转】_反编译_40

您也使用托管C++吗? 【转】_反编译_41    public:

您也使用托管C++吗? 【转】_c#_42        property String ^  get_PropertyA {

您也使用托管C++吗? 【转】_c++_43            String ^ get() {

您也使用托管C++吗? 【转】_c#_44                return gcnew String( m_Impl->GetPropertyA());

您也使用托管C++吗? 【转】_#pragma_45            }

您也使用托管C++吗? 【转】_c++_46        }


您也使用托管C++吗? 【转】_反编译_47        void MethodB( String ^ theString ) {

您也使用托管C++吗? 【转】_反编译_48            pin_ptr<const WCHAR> str = PtrToStringChars(theString);

您也使用托管C++吗? 【转】_c#_49            m_Impl->MethodB(str);

您也使用托管C++吗? 【转】_反编译_50        }

您也使用托管C++吗? 【转】_反编译_51

您也使用托管C++吗? 【转】_#pragma_52    private:

您也使用托管C++吗? 【转】_.net_53        UnmanagedClass * m_Impl;

您也使用托管C++吗? 【转】_.net_54    };

您也使用托管C++吗? 【转】_.net_55}


  然后,改变编译选项为“使用公共语言扩展 /clr”就可以了。这样,我们把代码编译成DLL文件就可以供.NET其它语言调用了。

  最后,C#中可以象如下的代码一样调用C++类了:

= new ManagedClass();

您也使用托管C++吗? 【转】_#pragma_56mc.MethoB("Hello");

您也使用托管C++吗? 【转】_反编译_57string s = mc.get_PropertyA;


  场景四:如何在托管C++代码中混合托管和非托管代码


  很简单,只要从#pragma unmanaged编译指示开始的程序,一率编译成非托管代码;要想恢复成托管代码,只要使用#pragma managed就可以了。如:


您也使用托管C++吗? 【转】_.net_58

您也使用托管C++吗? 【转】_反编译_59#include <iostream>

您也使用托管C++吗? 【转】_c#_60using namespace std;

您也使用托管C++吗? 【转】_.net_61

您也使用托管C++吗? 【转】_#pragma_62template<typename T>

您也使用托管C++吗? 【转】_.net_63void f(T t){

您也使用托管C++吗? 【转】_#pragma_64    cout << t << endl;

您也使用托管C++吗? 【转】_.net_65}

您也使用托管C++吗? 【转】_#pragma_66

您也使用托管C++吗? 【转】_.net_67#pragma managed

您也使用托管C++吗? 【转】_c#_68

您也使用托管C++吗? 【转】_#pragma_69using namespace System;

您也使用托管C++吗? 【转】_c++_70

您也使用托管C++吗? 【转】_反编译_71void m(String ^ s){

    Console::WriteLine(s);

您也使用托管C++吗? 【转】_反编译_72}

您也使用托管C++吗? 【转】_反编译_73

void main(){

您也使用托管C++吗? 【转】_反编译_74    f("Hello");

您也使用托管C++吗? 【转】_反编译_75    m("World");

}


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

=MethodCodeType.Native), SuppressUnmanagedCodeSecurity]

您也使用托管C++吗? 【转】_.net_76public static unsafe void modopt(CallConvCdecl) f<char const *>(sbyte modopt(IsSignUnspecifiedByte) modopt(IsConst)*);


  看不到源码,而方法属性标记为Unmanaged。

  如果没有加上#pragma unmanaged,反编译得到的 f 函数为:

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

您也使用托管C++吗? 【转】_.net_77{

您也使用托管C++吗? 【转】_c#_78      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);

您也使用托管C++吗? 【转】_.net_79}

  其中的函数内容一目了然。如果你的函数没有调用operator等不好理解的类库,那么反编译出来的代码简直和源码没差别。 


  场景五:不想要DLL,能不能直接把C++源代码与C#源代码一起编译成一个单独的Assembly呢?

  当然是可以的。具体参见:​​让C++源码和C#源码一起生成单一的Assembly​

  开心一刻:我只会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++源码就这么丢了,否则后果自负。