NET Framework 是一种新的计算平台,它简化了在高度分布式 Internet 环境中的应用程序开发。.NET Framework 旨在实现下列目标: 提供一个一致的面向对象的编程环境,而无论对象代码是在本地存储和执行,还是在本地执行但在 Internet 上分布,或者是在远程执行的。 提供一个将软件部署和版本控制冲突最小化的代码执行环境。 提供一个保证代码(包括由未知的或不完全受信任的第三方创建的代码)安全执行的代码执行环境。 提供一个可消除脚本环境或解释环境的性能问题的代码执行环境。 使开发人员的经验在面对类型大不相同的应用程序(如基于 Windows 的应用程序和基于 Web 的应用程序)时保持一致。
按照工业标准生成所有通信,以确保基于 .NET Framework 的代码可与任何其他代码集成。
.
NET Framework 具有两个主要组件:公共语言运行库和 .NET Framework 类库。公共语言运行库是 .NET Framework 的基础。
您可以将运行库看作一个在执行时管理代码的代理,它提供核心服务(如内存管理、线程管理和远程处理),而且还强制实施严格的类型安全以及可确保安全性和可靠性的其他形式的代码准确性。事实上,代码管理的概念是运行库的基本原则。
以
运行库为目标的代码称为托管代码,而不以运行库为目标的代码称为非托管代码。
.
NET Framework 的另一个主要组件是类库,它是一个综合性的面向对象的可重用类型集合,您可以使用它开发多种应用程序,这些应用程序包括传统的命令行或图形用户界面 (GUI) 应用程序,也包括基于 ASP.NET 所提供的最新创新的应用程序(如 Web 窗体和 XML Web services)。 .NET Framework 可由非托管组件承载,这些组件将公共语言运行库加载到它们的进程中并启动托管代码的执行,从而创建一个可以同时利用托管和非托管功能的软件环境。.NET Framework 不但提供若干个运行库宿主,而且还支持第三方运行库宿主的开发。 例如,ASP.NET 承载运行库以为托管代码提供可伸缩的服务器端环境。ASP.NET 直接使用运行库以启用 ASP.NET 应用程序和 XML Web services(本主题稍后将对这两者进行讨论)。 Internet Explorer 是承载运行库(以 MIME 类型扩展的形式)的非托管应用程序的一个示例。使用 Internet Explorer 承载运行库使您能够在 HTML 文档中嵌入托管组件或 Windows 窗体控件。以这种方式承载运行库使得托管移动代码(类似于 Microsoft? ActiveX? 控件)成为可能,但是它具有只有托管代码才能提供的重大改进(如不完全受信任的执行和安全的独立文件存储)。
转向.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属性包装出函数来调用。如:
[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站点,收集这方面的资料:,对于常用的函数甚至有完整的应用例子和帮助。当然,如果你有相应的资料和例子,你也可以贡献你的力量,给其它人帮助。
场景二
:用托管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等不好理解的类库,那么反编译出来的代码简直和源码没差别。
场景五
:不想要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++源码就这么丢了,否则后果自负。
广播电视节目制作经营许可证(京) 字第1234号 中国互联网协会会员