登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 程序员学前班[不再更新,只读] >> 主题: 托管/非托管 C++ 的切换和共存     [回主站]     [分站链接]
标题
托管/非托管 C++ 的切换和共存
clq
浏览(1) + 2010-04-04 19:41:12 发表 编辑

关键字:

托管/非托管 C++ 的切换和共存

http://qingtian881223.javaeye.com/blog/526405

这篇文章比较有意思。

clq
2010-4-4 19:44:15 发表 编辑

其实托管变成一个习惯叫法了,本意上的“托管”已经被 C++/CLI 标准代替了。 /clr 则是其在 vc 中的编译开关。

clq
2010-4-4 19:44:51 发表 编辑


2009-11-25
使用托管C++

http://ly4cn.teeta.com/blog/data/77172.html
您也使用托管C++吗?(转载)2008年08月21日 星期四 下午 04:02  转向.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++源码就这么丢了,否则后果自负。

-------
部份例子来自MSDN.转载自“http://ly4cn.teeta.com/blog/data/77172.html”

clq
2010-4-4 21:23:30 发表 编辑

zt
--------------------------------------------------
 C++/CLI 编程一些基本概念
 这里列举了一些我认为C++/CLI编程的需要知道一些基本概念

非安全代码(Unsafe code):一般而言,用VB.NET, C#编译成的代码是安全代码,这里的安全是指编译器本身的能力而言。比如对同样的一个C#程序,如果使用/unsafe选项进行编译的话,会产生如下的directive:

.assembly assemblyNameXXX

{

// 涉及到安全许可时,忽略代码校验

 .permissionset reqmin

             = {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}

}

// 此定制属性表明了非安全代码

.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )

对JIT来说,在编译为本机代码时,忽略代码校验。

有一点需要注意,非安全代码并不是说你的代码不“安全”,而是指从编译器到CLR无法保证或不负责代码的安全而已。

对于C++/CLI,只有/clr:safe编译选项才是安全代码。/clr:pure与C#中的/unsafe等同。而/clr选项为C++/CLI所特有。

传统意义上的C++代码统称为本地代码(native code)。本地代码当然是一种非安全代码。

托管代码(Managed Code):可以编译为中间语言(Intermediate Lanauage)的代码。与之相对的是非托管代码(Unmanaged Code)。对VB.net和C#而言,只能编译为托管代码。而C++/CLI则可将编译为托管代码和非托管代码的混合体。

托管数据(Managed Data):在托管堆上分配内存并由垃圾处理器负责回收的数据。

C++/CLI的四个面向CLR的编译选项:

- /clr:oldSyntax

兼容.NET 1.0 & 1.1的一种扩展语法,一般称为Managed Extension for C++。可以说是MS在.NET到来后,准备扩展C++的一种尝试。它不属于CLI的一部分。在以后的编译环境中可能会被淘汰。

- /clr

第一个符合CLI的C++扩展。用此编译选项可以产生混合代码。

/clr:pure

可以包含非托管数据,但不能包含非托管代码。

/clr:safe

只能包含托管代码和托管数据。

为什么需要C++/CLI?

就我的经验而言,对/clr:pure和/clr:safe编译选项而言,我更喜欢用C#来实现,无论从代码的优雅还是编程习惯或个人偏好来讲。C#也是微软在.net平台上主推的一门语言。唯一的区别仅是语法不同而已。一般而言,新的语言总能综全先前的一些语言的优点再加以扩展,因此更具有时代特征。还有在新的.NET 2.0,C++/CLI不允许开发Web应用程序,但可以用来开发WebService程序。

但C++/CLI毕竟有自己的"法宝"--/clr编译选项可以产生混合代码,也就是可以将托管和非托管共存。别小瞧这个功能,这个功能非常有用。

   1. 相信大多数公司都有一些旧的C++代码。在C++大行其道的时代,我们在C++上已经进行了大量投资,也许也产生了不少稳定的产品。在.NET大潮汹涌的今天,我们有必要重写这些代码吗?当然没有必要,要知道Legacy代码,并非指代码有问题,相反大多数这样的代码是成熟的产品,也经过了多外的检测。如果重写的话,不说工作量有多少,Bug肯定会引入不少,还要重新进行测试。可以说是出力不讨好。
   2. .NET的出现不会把C++逼入绝境,在一些对速度有要求的场合如算法的实现,模式的识别,驱动程序等,还得要C++。任意语言都不是完美的。虽然我们都在追求完美,但是完美并不存在。所以我们也要进行一些权衡。
   3. 有时我们需要的第三方软件只有C++接口,我们没有源代码,所以我们没有重写代码的可能。但我们通过C++/CLI可以写一个Wrapper来供.NET使用。
   4. 有时出于代码安全性考虑,把一些敏感算法用非托管代码实现,毕竟托管代码很容易被反编译。

公共语言运行时(CLR):

早在.NET出现之前,就有类似的概念,如VB6.0中的msvbvm60.dll,以及在java世界中的JVM(Jave Virtual Machine)。.net正是咨询和综合了业界的在这方面的经验,而开发出来的一个为.NET提供加载、校验、执行以及提供一些基础服务的运行时环境。当然,它也是对操作系统的抽象,也就是说,它的目标也是"Write once, run anywhere"。现在也有一些开源的项目在尝试在linux等平台上也提供类似的运行时环境,典型的如mono(http://www.mono-project.com/Main_Page)。

clq
2010-4-4 23:08:05 发表 编辑

在编译时,会遇到编译错误。需要设置下:

1. 项目属性—配置属性—常规—字符集,选择多字节字符集。VC2005默认是Unicode。
2. 提示错误error C2871: “System”: 具有该名称的命名空间不存在时,是因为程序是托管代码,在项目中选择公共语言运行库支持/clr就好了。设置路径就在设置字符集的下面一栏。
3. 提示错误error LNK2001: unresolved external symbol _WinMain@16。参照:vc中error LNK2001:unresolved external symbol _WinMain@16的解决方法



总数:4 页次:1/1 首页 尾页  
总数:4 页次:1/1 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1