且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《.Ne框架程序设计》随记(2)

更新时间:2022-09-16 16:41:07

 CLR直接打交道的并不是托管模块,而是程序集(assembly). 程序集是一个或多个托管模块,以及一些资源文件的逻辑组合。其次,程序集是组件复用以及实施安全策略,版本策略的最小单位。它可以是一个或多个文件组成。

       托管模块有其自身的元数据来描述自己包含的类型以及其引用的其他类型,而程序集中的清单(manifest)则描述了组成程序集的文件的信息。也可以这么说,若是单个的托管模块,程序集就是一个包含了清单的托管模块,这种情况下程序集也没有包含任何资源文件。而若想把一组文件合并到一个程序集中的话,就需要使用程序集链接器(Assembly Linker即AL.exe)等工具。

       对于一个可重用,可部署,可实施版本管理的组件来说,程序集允许我们分离它的逻辑表示和物理表示。例如,我们可以将一些很少使用的类型和资源放在单独的程序集文件中,而这些文件只在使用到的时候才从服务器下载到客户端,这就提高了效率。也就是说,程序集允许将文件的部署分解开,同时又将所有的文件看作是一个单独的集合。

       程序集中还包含它所引用的程序集的一些信息(如版本号)。这就使程序集能够实现自描述。也就是说,CLR可以通过这些信息知道执行所需要的所有内容,不用再从注册表或活动目录(Active Directory)中获取额外的信息。因此,程序集的部署比非托管组件容易很多,因为所有的部署所需要的信息都包含在程序集中。

       一个程序集或者是一个可执行程序,或者是一个包含供可执行程序使用的一组类型(组件)的DLL. CLR负责管理程序集中的代码执行。


     我们可以通过在系统盘中\WINDOWS\system32目录下是否存在mscoree.dll文件来判断机器中是否暗转安装了.net framework;若想知道机器中安装了哪些版本的.net framework.可以查看以下注册表键下的子键:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy


using System;
using System.IO;
using Microsoft.Win32;
using System.Text.RegularExpressions;

namespace ConsoleApplication1
{
    /// <summary>
    /// Class1 的摘要说明。
    /// </summary>
    class Class1
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            //
            // TODO: 在此处添加代码以启动应用程序
            //
            String pattern = @"v(\+|-)?[0-9][0-9]*(\.[0-9]*)?.(\+|-)?[0-9][0-9]*(\.[0-9]*)?";
            System.Text.RegularExpressions.Regex r = new Regex(pattern);
            System.Text.RegularExpressions.Match m;
            

            String filePath = @"C:\WINDOWS\system32\mscoree.dll";
            if(System.IO.File.Exists(filePath))
            {
                System.Console.Out.WriteLine("已经安装了.net framework!!!");
                Microsoft.Win32.RegistryKey key;
                key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\.NETFramework\Policy");
                String[] subNames = key.GetSubKeyNames();
                System.Console.Out.WriteLine("你机器上已经安装了.net framework的版本如下:");
                for(int i=0;i<subNames.Length;i++)
                {

                    m = r.Match(subNames[i].ToString());
                    if(m.Success)
                    {
                        System.Console.Out.WriteLine(subNames[i].ToString());
                    }
                }
            }
            else
            {
                System.Console.Out.WriteLine("未安装了.net framework!!!");
            }
        }
    }
}

      
     当生成一个exe程序集时,编译器/链接器会产生一些特殊信息,并将这些信息嵌入到结果程序集的PE文件头及其各个组成文件的.text部分。当exe文件被调用时,这些特殊信息就会导致CLR被加载并开始初始化。CLR随后就会定位到应用程序的入口点方法,从而来启动应用程序。
       IL代码是很容易被反汇编出源代码的,但对于web应用程序而言,这是没有什么影响的,而对于那些向外发布的托管模块来说,我们可以使用混淆器工具,或者选择在非托管模块中实现敏感算法,这些非托管模块中包含的是本地cpu指令,而不是IL代码和元数据,然后再利用CLR的互操作功能来实现托管代码和非托管代码之间的通信。
 
Public static void Main(String[] args)
{
Console.WriteLine(“hello”);
Console.WriteLine(“World”);
}



    在Main执行之前,CLR首先检查Main中代码引用到的所有类型,这就使CLR分配一个内部的数据结构,用于管理对所引用到的类型的访问。这里,Main只引用了一个Console类型,CLR为它分配一个内部的数据结构,Console类中定义的每个方法都会有一个对应的条目,每个条目中将保存有一个方法实现代码的地址,当该数据结构被初始化时,CLR 将把每一个条目设置为CLR内部的一个没有正式记录的函数,叫JITCompiler.当Main方法第一次调用WriteLine时,JITCompiler将被调用,该函数负责将一个方法的IL代码编译为本地CPU指令。当JITCompiler函数被调用时,它会在被调用方法定义的程序集中的元数据内搜索其IL代码的位置。JITCompiler再验证这些IL代码并把它们编译为本地CPU指令。这些指令被保存在一个动态分配的内存块中,然后JITCompiler将前面内部数据结构中被调用方法的地址替换为包含本地CPU指令的内存地址,最后JITCompiler将跳转到该内存块中的代码上(也就是WriteLine方法)。当第二次调用WriteLine时,由于WriteLine的IL代码已经被验证并且编译过,所以就直接调用内存块中已有的本地代码,完全跳过JITCompiler函数的验证和编译。

       托管代码的性能和非托管代码的性能相差不大,若托管代码的性能不好的话,可以用NGen将程序集中所有的IL代码转化为本地代码,并将结果代码保存在磁盘上的一个文件中。在运行时程序集被加载时, CLR将自动检查是否有该程序集的预编译版本存在,若存在,CLR将加载预编译的代码,不再需要额外的运行时编译。

       IL代码最大的好处在于编译为本地CPU指令时,CLR将执行一个验证过程,确保代码的安全。托管模块的元数据中包含了所有被验证过程使用的方法和类型信息,若IL代码被确定是“不安全”的,那么就抛出一个System.Security.VerificationException异常,阻止方法继续执行。为了确保托管模块中的方法包含的都是可验证安全的IL代码,可以使用PEVerify来进行验证。若验证过程被关闭,JIT编译器会把IL代码编译为本地CPU指令。 



本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/08/31/491521.html,如需转载请自行联系原作者