Microsoft Corporation
2001 年 7 月
摘要:本文包含有关 Microsoft .NET 和 Microsoft .NET 框架的常见问题。(共 15 页打印页)
概念性问题
运行库技术问题
术语
程序集
应用程序部署和隔离
垃圾回收
远程处理
互操作性
安全性
Microsoft .NET 框架是生成、部署和运行 Web 服务及应用程序的平台。它提供了一个生产率高且基于标准的多语言环境,用于将现有投资与下一代应用程序和服务集成,同时提供了解决 Internet 规模应用程序的部署和操作难题的灵活性。.NET 框架由三个主要部分组成:公共语言运行库、统一类库的分层集合和称为 ASP.NET 的 Active Server Pages 组件化版本。
公共语言运行库是用于 .NET 框架应用程序的执行引擎。
它提供若干服务,包括:
通用类型系统是一个内置在公共语言运行库中的丰富的类型系统,它支持大多数编程语言中存在的类型和操作。通用类型系统支持广泛的编程语言的完整实现。
公共语言规范是一组构造和约束,它充当库编写器和编译器编写器的向导。它使库完全可通过任何支持 CLS 的语言使用,并使那些语言可以相互集成。公共语言规范是通用类型系统的一个子集。对于正在编写将由其他开发人员使用的代码的应用程序开发人员,公共语言规范也非常重要。当开发人员按照 CLS 规则设计可公开访问的 API 时,这些 API 很容易通过面向公共语言运行库的所有其他编程语言使用。
MSIL 是与 CPU 无关的指令集,其中编译进了 .NET 框架程序。它包含在对象上加载、存储、初始化和调用方法的指令。
在与元数据及公共类型系统组合后,MSIL 允许真正的语言间集成。
在执行之前,MSIL 将转换成机器码,并且不被解释。
托管代码是为面向公共语言运行库的服务编写的代码(请参阅“什么是公共语言运行库?”)。为了面向这些服务,该代码必须向运行库提供最低级别的信息(元数据)。默认情况下,所有 C#、Visual Basic .NET 和 JScript .NET 代码都受托管。默认情况下,Visual Studio .NET C++ 代码不受托管,但编译器可以通过指定命令行开关 (/CLR) 来产生托管代码。
与托管代码密切相关的是托管数据(由公共语言运行库的垃圾回收器分配和释放的数据)。默认情况下,C#、Visual Basic 和 JScript .NET 数据受托管。然而,通过使用特殊的关键字可以将 C# 数据标记为非托管。默认情况下,Visual Studio .NET C++ 数据不受托管(即使在使用 /CLR 开关时),但在使用 C++ 托管扩展时,可以通过使用 __gc 关键字将类标记为托管。正如名称所暗示的,这意味着用于类实例的内存受垃圾回收器的托管。另外,类成为 .NET 框架社区的一名完全参与的成员,这既带来了好处,也带来了限制。其中一个好处是与用其他语言编写的类的正确互操作性(例如,托管 C++ 类可以从 Visual Basic 类继承)。其中一个限制是托管类只能从基类继承。
程序集是 .NET 框架应用程序的主要构造块。它是一个功能集合,并以单个实现单元(一个或多个文件)的形式生成、版本化和部署。所有托管类型和资源或者只在其实现单元中标记为可访问,或者在该单元外由代码标记为可访问。
程序集通过其清单(每个程序集不可缺少的部分)进行自我描述。清单可以:
该信息在运行时用于解析引用、强制版本绑定策略和验证加载程序集的完整性。运行库可以确定和定位任何运行对象的程序集,因为每个类型均加载在程序集的上下文中。程序集也是应用代码访问安全权限的单元。在确定给程序集包含的代码授予什么权限时,分别考虑每个程序集的标识证据。
程序集自我描述的特性也有助于使无影响安装和 XCOPY 部署可行。
专用程序集仅由单个应用程序使用,并存储在该应用程序的安装目录中(或其中的子目录中)。共享程序集是一个可以由多个应用程序引用的程序集。为了共享程序集,必须给予该程序集一个加密的强名称(有时又称强名称),为此目的显式生成程序集。相比之下,专用程序集名称只需在使用它的应用程序中是唯一的。
通过区分专用程序集和共享程序集,我们以显式决策的形式引入共享概念。只需将专用程序集部署到应用程序目录,即可保证该应用程序只与用来生成和部署它的位一起运行。对专用程序集的引用只被本地解析到专用应用程序目录。
可以举出多个生成和使用共享程序集的理由,如表示版本策略的能力。共享程序集有一个加密的强名称,这意味着只有此程序集的作者具有产生程序集新版本的密钥。因此,如果您生成一个策略语句,表明自己希望接受程序集的新版本,则应确信版本更新将受作者的控制和验证。否则,您不必接受它们。
对于本地安装的应用程序,共享程序集通常被显式安装到全局程序集缓存中(由 .NET 框架维护的程序集的本地缓存)。.NET 框架版本管理功能的关键是:下载的代码不影响本地安装的应用程序的执行。下载的代码放置在特殊的下载缓存中,并且在计算机上不是全局可用的,即使以共享程序集的形式生成了某些下载组件。
.NET 框架随附的类均以共享程序集的形式生成。
生成共享程序集的确涉及使用加密密钥。在生成程序集时,只有公钥是确实需要的。面向 .NET 框架的编译器在生成程序集时提供命令行选项(或使用自定义属性)来提供公钥。常见的做法是在源数据库中保留通用公钥的一个副本,并将生成脚本指向该密钥。在交付程序集前,必须用相应的私钥完全签名程序集。用称为 SN.exe(强名称)的 SDK 工具来完成此工作。
与 Authenticode 不同,强名称签名不涉及证书。不涉及第三方单位,没有要支付的费用,也没有证书链。另外,验证强名称比验证 Authenticode 的系统开销低得多。然而,强名称不就是否信任特定的发布者做出任何声明。强名称使您可以确保给定程序集的内容没有被篡改,并且在运行时以您的名义加载的程序集来自您开发时所针对的同一发布者。但它不就是否可以信任此发布者的标识做出任何声明。
命名空间是类型的逻辑命名方案,此方案中简单的类型名称(如 MyType)用以点分隔的分层名称开头。这样的命名方案完全受开发人员控制。例如,类型 MyCompany.FileAccess.A 和 MyCompany.FileAccess.B 可能在逻辑上应具有与文件访问相关的功能。.NET 框架使用分层命名方案将类型分为相关功能(例如 ASP.NET 应用程序框架)或远程处理功能的逻辑类别。设计工具可以利用命名空间使开发人员在代码中浏览和引用类型更容易。命名空间的概念与程序集的概念无关。单个程序集可以包含分层名称具有不同命名空间根的类型,而逻辑命名空间根可以跨越多个程序集。在 .NET 框架中,命名空间是逻辑设计时命名便利,而程序集在运行时建立类型的名称范围。
.NET 框架通过使应用程序的无影响安装和 XCOPY 部署切实可行来简化部署。由于所有请求均首先解析到专用应用程序目录,只需将应用程序的目录文件复制到磁盘便可运行该应用程序。不需要任何注册。
该方案对 Web 应用程序、Web 服务和独立的桌面应用程序尤为迫切。然而,在某些情况下,XCOPY 作为分发机制是不充分的。应用程序只有很少的专用代码并且依赖于共享程序集的可用性,或者应用程序不是在本地安装的(而是按需下载的)便属于这种情况。在上述情况下,.NET 框架提供大量的代码下载服务以及与 Windows 安装程序的集成。相对于当前平台,.NET 框架提供的代码下载支持提供了一些优点,包括增量下载、代码访问安全性(没有其他 Authenticode 对话框)和应用程序隔离(代表一个应用程序下载的代码不影响其他应用程序)。Windows 安装程序是另一个可用于 .NET 应用程序的功能强大的部署机制。Windows 安装程序的所有功能(包括发布、公布和应用程序修复)均适用于 Windows 安装程序 2.0 中的 .NET 应用程序。
将要由多个应用程序使用的程序集(如共享程序集)被部署到全局程序集缓存中。在预发布版和 Beta 版中,使用 GACUtil SDK 工具的 /i 选项将程序集安装到缓存中:
gacutil /i myDll.dll
Windows Installer 2.0(随附在 Windows XP 和 Visual Studio .NET 中)能够将程序集安装到全局程序集缓存。
.NET 框架提供了一个用于查看程序集缓存的 Windows 外壳程序扩展。通过 Windows 资源管理器导航至 % windir%\assembly 将激活查看器。
应用程序域(通常为 AppDomain)是一个用于隔离应用程序的虚拟进程。在同一应用程序范围内(换句话说,以应用程序入口点开头的对象激活序列中的任何位置)创建的所有对象都创建在同一应用程序域中。多个应用程序域可以存在于单个操作系统进程中,这使它们成为应用程序隔离的轻量方法。
操作系统进程通过提供一个独特的内存地址空间来提供隔离。虽然这很有效,但成本很高,而且不能扩展到大型 Web 服务器所需的数目。另一方面,公共语言运行库通过管理运行在应用程序域中的代码的内存使用来强制应用程序隔离。这可确保它不会访问域边界以外的内存。注意只有类型安全代码才能以这种方式进行管理(运行库在不安全代码加载到应用程序域中时无法保证隔离)很重要。
垃圾回收是一种机制,它使计算机能够检测到对象何时不能再被访问。然后它自动释放该对象所使用的内存(同时调用由用户编写的称为“终结程序”的清理例程)。有些垃圾回收器(如 .NET 使用的垃圾回收器)压缩内存,从而减小程序的工作集。
对于大多数程序员而言,拥有垃圾回收器(并使用垃圾回收对象)意味着即使使用复杂的数据结构,也不必担心释放内存或引用计数对象。但是,如果您通常在释放对象内存的同一代码块中释放系统资源(文件句柄、锁等等),则确实需要在编码样式方面进行某些更改。对于垃圾回收对象,应提供一种以确定方式(即受程序的控制)释放系统资源的方法,并使垃圾回收器可以在压缩工作集时释放内存。
所有面向运行库的语言都允许从垃圾回收堆分配类对象。这带来了快速分配方面的好处,并且使程序员不必计算出何时应显式“释放”每个对象。
CLR 还提供所谓的 ValueType。ValueType 与类相似,不同的是 ValueType 对象在运行时堆栈(而不是堆)上分配,因此当代码退出定义了这些对象的过程时它们将被自动回收。这便是 C# 中“结构”的操作方式。
C++ 托管扩展使您可以选择分配类对象的位置。如果由 __gc 关键字声明为托管类,则它们从垃圾回收堆中分配。如果它们不包括 __gc 关键字,则它们的行为和常规 C++ 对象类似,都是从 C++ 堆分配,并且由“free”方法显式释放。
有关垃圾回收的附加信息,请参阅:
进程内通讯有两个方面:单个应用程序域中的上下文之间的通讯或应用程序域之间的通讯。在同一应用程序域的上下文之间,代理用作侦听机制。不涉及任何封送处理或序列化操作。在跨越多个应用程序域时,我们使用运行时二进制协议进行封送处理/序列化。
进程间通讯使用可插接式信道和格式化程序协议,其中每个协议适用于特定的目的。
具有 SOAP 格式化程序的 HTTP 信道(HTTP 非常适合在 Internet 上使用或者通讯必须经过防火墙的情况)
具有二进制格式化程序的 TCP 信道(TCP 是用于局域网 (LAN) 的性能更高的选项)
当在托管代码和非托管代码间进行转换时,COM 结构(确切地说是 DCOM)用于远程处理。在 CLR 的中间版本中,这通过适用于被服务的组件(使用 COM+ 服务的组件)。在最终发布时,应该可以配置任何可远程处理的组件。
对象的分布式垃圾回收由称为“基于租约的生存期”的系统托管。每个对象均有一个租用时间,当时间过期时,对象会与 CLR 的远程处理结构断开连接。对象都有默认的续订时间,当从客户端成功地调用了对象时续订该租约。客户端也可以显式续订租约。
可以。现已部署的任何 COM 组件均可通过托管代码中使用,通常情况下,改编是完全自动的。
具体说来,自动行为是使用运行库可调用的包装 (RCW) 从 .NET 框架访问 COM 组件。该包装将 COM 组件公开的 COM 接口转换成与 .NET 框架兼容的接口。对于 OLE 自动化接口,RCW 可以从类型库中自动生成。而对于非 OLE 自动化接口,开发人员可以编写自定义 RCW,并将 COM 接口公开的类型手动映射至与 .NET 框架兼容的类型。
可以。现已生成的托管类型可通过 COM 访问,通常情况下,配置是完全自动的。托管开发环境的某些新功能无法通过 COM 访问。例如,静态方法和参数化构造函数无法通过 COM 使用。通常情况下,最好提前确定给定类型的预期用户。如果要通过 COM 使用类型,则可能只限于使用 COM 可访问的功能。
根据用于编写托管类型的语言,默认情况下,该类型可能是可见的,也可能是不可见的。
具体说来,自动行为是使用 COM 可调用的包装 (CCW) 从 COM 访问 .NET 框架组件。这与 RCW 类似(请参阅前一个问题),但工作方向相反。此外,如果 .NET 框架开发工具无法自动生成包装,或者如果自动行为不是您所要的,则可以开发自定义 CCW。
可以。使用平台调用,.NET 框架程序可以通过静态 DLL 入口点访问本机代码库。
下面是一个调用 Win32 MessageBox 函数的 C# 示例:
using System; using System.Runtime.InteropServices; class MainApp { [DllImport("user32.dll", EntryPoint="MessageBox")] public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType); public static void Main() { MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 ); } }
这通常不是问题,大多数应用程序将安全运行,并且不会被恶意攻击所利用。只需使用标准的类库访问资源(如文件)或执行受保护的操作(如类型的私有成员反射),安全性便由这些库得到加强。开发人员可能需要做的一件简单的事是包括权限请求(声明安全性的形式),以限制代码可以接收的权限(限制为它需要的权限)。这还可确保在允许代码运行的情况下,只有具备了所需的所有权限才能这样做。
只有编写新基类库公开新的资源类型的开发人员才需要直接使用安全系统。不是使所有的代码都有潜在的安全危险,代码访问安全性将危险限制在显式重写安全系统的极少量代码上。
默认的安全策略只向来自本地 Intranet 区域的代码授予一组有限的权限。该区域由 Internet Explorer 安全设置定义,并且应配置为与企业中的本地网络匹配。由于由 UNC 或映射驱动器命名(如用 NET USE 命令)的文件通过该本地网络发送,这些文件也在本地 Intranet 区域中。
默认值是针对不安全 Intranet 的最坏情况设置的。如果您的 Intranet 比较安全,则可以修改安全策略(用 .NET 框架配置工具或 CASPol 工具),向本地 Intranet 或它的各部分(例如特定的计算机共享名称)授予更多权限。
当代码试图执行无权执行的操作时会发生安全性异常。权限的授予基于已知的代码情况,尤其是它的位置。例如,从 Internet 运行的代码比从本地计算机运行的代码获得的权限少,因为经验证明前者通常不如后者可靠。因此,为了使因安全性异常而失败的代码运行,必须增加授予它的权限。这样做的一个简单方法是将代码移至更受信任的位置(如本地文件系统)。但这并不适用于所有情况(Web 应用程序便是一个很好的例子,公司网络上的 Intranet 应用程序是另一个例子)。因此,如果不改变代码的位置,还可以更改安全策略,向该位置授予更多权限。使用 .NET 框架配置工具或代码访问安全策略实用工具(caspol.exe)都可以做到这一点。如果您是代码开发人员或发布者,则还可以为代码加上数字签名,然后修改安全策略,向带该签名的代码授予更多权限。但是,在采用上述任何操作时,请记住给代码授予的权限要少些,这是因为它不是来自可识别的可靠源。在将代码移至本地计算机或更改安全策略前,应确保信任代码不会执行恶意的或损害性操作。
.NET 框架包括 .NET 框架配置工具——MMC 管理单元(mscorcfg.msc)以配置包括安全策略在内的 CLR 的若干方面。该管理单元不仅支持在本地计算机上管理安全策略,而且还创建与系统管理服务器和组策略兼容的企业策略部署包。命令行实用工具 CASPol.exe 也可用于计算机上的脚本策略更改。为了在命令提示中运行任一种工具,请将当前目录更改为 .NET 框架的安装目录(位于 %windir%\Microsoft.Net\Framework\v1.0.2914.16\)并键入 mscorcfg.msc 或 caspol.exe。
基于证据的安全性(它授权代码)与 Windows 2000 安全性(它基于登录标识)一起使用。例如,为访问文件,托管代码必须既有代码访问安全性文件权限,还必须在具有 NTFS 文件访问权的登录标识下运行。.NET 框架中包含的托管库还为基于角色的安全性提供了类。这些类使应用程序可以使用 Windows 登录标识和用户组。
本文地址:http://com.8s8s.com/it/it46125.htm