`
彭泽1991
  • 浏览: 2348 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

DotNet程序集解析

    博客分类:
  • C#
阅读更多

   在.NET Framework框架中,程序集是重用、安全性以及版本控制的最小单元。程序集的定义为:程序集是一个或多个类型定义文件及资源文件的集合。程序集主要包含:PE/COFF,CLR头,元数据,清单,CIL代码,元数据。

   PE/COFF文件是由工具生成的,表示文件的逻辑分组。PE文件包含“清单”数据块,清单是由元数据表构成的另一种集合,这些表描述了构成程序集的文件,由程序集中的文件实现的公开导出的类型,以及与程序集关联在一起的资源或数据文件。

   在托管程序集中包含元数据和IL(微软的一种中间语言),IL能够访问和操作对象类型,并提供了指令来创建和初始化对象、调用对象上的虚方法以及直接操作数组元素。

   CLR头是一个小的信息块,主要包含模块在生成是所面向的CLR的major(主)和major(次)版本号;一个标志,一个MethodDef token(指定了模块的入口方法);一个可选的强名称数字签名。

   元数据表示一个二进制数据块,由几个表构成:定义表,引用表,清单表。

   以上是对程序集的构成做了一个简单的说明,接下来看一下程序集的一些特性:程序集定义了可重用的类型;程序集标记了一个版本号;程序集可以有关联的安全信息。

  在程序运行时,JIT编译器利用程序集的TypeRef和AssemblyRef元数据表来确定哪一个程序集定义了所引用的类型。JIT编译器在运行时需要获取程序集的相关信息,主要包括:名称、版本、语言文化、公钥标记等,并将这些连接为一个字符串。JIT编译器会差查找该标识的程序集,如果查询到,则将该程序集加载到AppDomain。

   接下来介绍一下在CLR中加载程序集的方法:

    在System.Refection.Assembly类的静态方法Load来加载程序集,在加载指定程序集的操作中,会使用LoadFrom()方法,LoadFrom()具有多个重载版本,看一下LoadFrom这个方法的底层实现代码:

[ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        [MethodImplAttribute(MethodImplOptions.NoInlining)]
        public static Assembly LoadFrom(String assemblyFile) 
        {
            Contract.Ensures(Contract.Result<Assembly>() != null); 
            Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);

            StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
 
            return RuntimeAssembly.InternalLoadFrom(
                assemblyFile, 
                null, // securityEvidence 
                null, // hashValue
                AssemblyHashAlgorithm.None, 
                false,// forIntrospection
                false,// suppressSecurityChecks
                ref stackMark);
        }
[System.Security.SecurityCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable 
        internal static RuntimeAssembly InternalLoadFrom(String assemblyFile, 
                                                         Evidence securityEvidence,
                                                         byte[] hashValue, 
                                                         AssemblyHashAlgorithm hashAlgorithm,
                                                         bool forIntrospection,
                                                         bool suppressSecurityChecks,
                                                         ref StackCrawlMark stackMark) 
        {
            if (assemblyFile == null) 
                throw new ArgumentNullException("assemblyFile"); 

            Contract.EndContractBlock(); 

#if FEATURE_CAS_POLICY
            if (securityEvidence != null && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)
            { 
                throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));
            } 
#endif // FEATURE_CAS_POLICY 
            AssemblyName an = new AssemblyName();
            an.CodeBase = assemblyFile; 
            an.SetHashControl(hashValue, hashAlgorithm);
            // The stack mark is used for MDA filtering
            return InternalLoadAssemblyName(an, securityEvidence, null, ref stackMark, true /*thrownOnFileNotFound*/, forIntrospection, suppressSecurityChecks);
        }

   在加载程序集的操作中,LoadFrom首先会调用Syatem.Reflection.AssemblyName类的静态方法GetAssemblyName(该方法打开指定文件,查找AssemblyRef元数据表的记录项,提取程序集标识信息,然后以一个Syatem.Reflection.AssemblyName对象的形式返回这些信息),LoadFrom方法在内部调用Assembly的Load方法,将AssemblyName对象传给它,CLR会为应用版本绑定重定向策略,并在各个位置查找匹配的程序集。如果Load找到匹配的程序集,就会加载它,并返回代表已加载程序集的一个Assembly对象,LoadFrom方法将返回这个值。

 

    加载程序的另一个方法为LoadFile,这个方法可从任意路径加载一个程序集,并可将具有相同标识的一个程序集多次加载到一个AppDoamin中。接下来可以看一下LoadFile的底层实现代码:

[System.Security.SecuritySafeCritical]  // auto-generated
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        public static Assembly LoadFile(String path) 
        {
 
            Contract.Ensures(Contract.Result<Assembly>() != null); 
            Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);
 
            AppDomain.CheckLoadFileSupported();

            new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, path).Demand();
            return RuntimeAssembly.nLoadFile(path, null); 
        }

  以上对程序集的结构和程序集的加载方法做了一个简单的说明,需要说明的一点是:程序集不提供卸载的功能。

    以下提供几种较为常用的程序集操作方法:

       1.公共属性和方法:

public static int Minutes = 60;
        public static int Hour = 60 * 60;
        public static int Day = 60 * 60 * 24;
        private readonly int _time;
        private bool IsCache { get { return _time > 0; } }

        /// <summary>
        /// 缓存时间,0为不缓存(默认值:0秒,单位:秒)
        /// </summary>
        public ReflectionSugar(int time = 0)
        {
            _time = time;
        }

        /// <summary>
        /// 根据程序集路径和名称获取key
        /// </summary>
        /// <param name="keyElementArray"></param>
        /// <returns></returns>
        private string GetKey(params string[] keyElementArray)
        {
            return string.Join("", keyElementArray);
        }

        /// <summary>        
        /// key是否存在      
        /// </summary>        
        /// <param name="key">key</param>        
        /// <returns>存在<c>true</c> 不存在<c>false</c>. </returns>        
        private bool ContainsKey(string key)
        {
            return HttpRuntime.Cache[key] != null;
        }

        /// <summary>        
        ///获取Cache根据key 
        /// </summary>                
        private V Get<V>(string key)
        {
            return (V)HttpRuntime.Cache[key];
        }

        /// <summary>        
        /// 插入缓存.        
        /// </summary>        
        /// <param name="key">key</param>        
        /// <param name="value">value</param>        
        /// <param name="cacheDurationInSeconds">过期时间单位秒</param>        
        /// <param name="priority">缓存项属性</param>        
        private void Add<TV>(string key, TV value, int cacheDurationInSeconds, CacheItemPriority priority = CacheItemPriority.Default)
        {
            string keyString = key;
            HttpRuntime.Cache.Insert(keyString, value, null, DateTime.Now.AddSeconds(cacheDurationInSeconds), Cache.NoSlidingExpiration, priority, null);
        }

  2.加载程序集:

/// <summary>
        /// 加载程序集
        /// </summary>
        /// <param name="path">程序集路径</param>
        /// <returns></returns>
        public Assembly LoadFile(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException(path);
            }
            try
            {
                var key = GetKey("LoadFile", path);
                if (IsCache)
                {
                    if (ContainsKey(key))
                    {
                        return Get<Assembly>(key);
                    }
                }

                var asm = Assembly.LoadFile(path);
                if (IsCache)
                {
                    Add(key, asm, _time);
                }

                return asm;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

   3.根据程序集获取类型:

        /// <summary>
        /// 根据程序集获取类型
        /// </summary>
        /// <param name="asm">Assembly对象</param>
        /// <param name="nameSpace">命名空间</param>
        /// <param name="className">类名</param>
        /// <returns>程序集类型</returns>
        public Type GetTypeByAssembly(Assembly asm, string nameSpace, string className)
        {
            try
            {
                var key = GetKey("GetTypeByAssembly", nameSpace, className);
                if (IsCache)
                {
                    if (ContainsKey(key))
                    {
                        return Get<Type>(key);
                    }
                }

                Type type = asm.GetType(nameSpace + "." + className);
                if (IsCache)
                {
                    Add(key, type, _time);
                }
                return type;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

 4. 创建对象实例:

        /// <summary>
        /// 创建对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fullName">命名空间.类型名</param>
        /// <param name="assemblyName">程序集(dll名称)</param>
        /// <returns></returns>
        public T CreateInstance<T>(string fullName, string assemblyName)
        {
            var key = GetKey("CreateInstance1", fullName, assemblyName);
            if (IsCache)
                if (ContainsKey(key))
                {
                    return Get<T>(key);
                }
            //命名空间.类型名,程序集
            var path = fullName + "," + assemblyName;
            //加载类型
            var o = Type.GetType(path);
            //根据类型创建实例
            var obj = Activator.CreateInstance(o, true);
            var reval = (T)obj;
            if (IsCache)
                Add<T>(key, reval, _time);
            //类型转换并返回
            return reval;
        }

 以上的方法中,根据加载的程序集创建对象后,将获取的返回值结构加入缓存中。

  备注:由于博客搬家出现问题,有关本人的其他.NET文章,请访问:http://www.cnblogs.com/pengze0902/

分享到:
评论

相关推荐

    dis#_dotNet程序集反混淆工具

    "dis#_dotNet程序集反混淆工具"是一款专门针对由dotNet编译的.exe和.dll文件的反混淆工具。混淆是在软件发布时常用的一种保护手段,通过给代码命名和结构打乱,使得逆向工程变得困难,从而防止源代码被轻易分析。...

    DotNet程序打包助手

    《 DotNet程序打包助手:深度解析与应用指南》 在软件开发领域,尤其是.NET平台下,程序员们常常面临一个问题:如何有效地将多个exe和dll文件整合成一个单一的可执行程序,以方便用户使用和保护代码。这时,"DotNet...

    DotNet反混淆工具集

    `GAC Verifier (检测GAC程序集是否被篡改)` 是一个用于检查全局程序集缓存(Global Assembly Cache, GAC)中的程序集是否被篡改的工具。在.NET环境中,GAC用于存储共享的.NET组件,确保它们在多应用间的兼容性和安全...

    dotnet 使用 Refasmer 从现有的 DLL 里面导出公开的成员组装出新的仅作为引用用途的程序集.rar

    Refasmer的主要功能是解析DLL中的IL代码,分析并提取公开的类型、方法、属性等成员,然后将这些成员重新组合成一个新的程序集。这样做的好处是可以减少最终应用的大小,提高加载速度,同时降低依赖冲突的可能性。 ...

    dotnet 使用 dnlib 检测插件程序集的 API 兼容性.rar

    使用dnlib,我们可以深入解析程序集的元数据,检查类型、方法、属性等元素,从而判断它们是否兼容目标框架或API集。 API兼容性检查通常包括以下几个步骤: 1. **加载程序集**:使用dnlib的`ModuleDefMD`类加载待...

    dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承.rar

    这个问题通常涉及到程序集引用、命名空间管理以及编译时的类型解析。本文将深入探讨这个问题,并提供解决方案。 首先,理解.NET程序集的概念至关重要。程序集是.NET框架中的基本部署和重用单元,包含元数据和IL...

    dotnet最全的框架合集

    《.NET框架全面解析》 本文将深入探讨.NET框架,这是一个由微软开发并维护的开源开发平台,广泛用于构建各种类型的软件应用。.NET框架包含了丰富的类库、开发工具和运行环境,支持多种编程语言,如C#、VB.NET、F#等...

    dotnet 使用增量源代码生成技术的 Telescope 库导出程序集类型.rar

    总之,"dotnet 使用增量源代码生成技术的 Telescope 库导出程序集类型"这一主题展示了如何通过先进的.NET技术优化开发流程,提高代码质量和效率。在MAUI框架的开发中,这种技术的应用将带来更加流畅和高效的开发体验...

    C#中通过Assembly类访问程序集信息

    ### C#中通过Assembly类访问程序集信息 在.NET框架中,程序集是部署、版本控制、安全性以及重用的基本单元。程序集包含了编译后的代码(中间语言IL)及元数据,它由一个或多个模块组成。在C#中,`Assembly`类提供了...

    DotNet Resolver v3.3.0.0

    有了这个,你可以在加载程序集追查面向对象编程(OOP)的结构,并找出如何组件被很容易地建立。 插件支持 以及许多其他。NET反编译器,旋转变压器的DotNet得到了一个插件系统,它允许你创建扩展应用程序。该系统...

    查看程序依赖的程序集,便于调试查找缺省的依赖文件

    - **dotnet CLI的`dotnet restore`和`dotnet build`命令**:这些命令在构建过程中会自动解析依赖,并生成项目.lock文件,列出所有依赖项。 - **NuGet Package Explorer**:这个工具可以查看NuGet包的结构和依赖...

    C#实用类集-DotNet.Utilities

    DotNet.Utilities可能提供了一套XML处理工具,帮助开发者轻松解析XML文档,查找、修改或添加XML节点。这可能包括读取XML文件,转换为对象,或者将对象序列化为XML,同时可能还支持XPath查询和XSLT转换,以实现更复杂...

    DotNet Helper(.NET程序反编译器) v2.1 简体中文版

    【描述】: DotNet Helper v2.1 是一款面向中文用户的高效工具,它提供了对.NET程序集的深度分析和反编译能力。通过这款软件,用户可以查看.NET程序的元数据,包括类、方法、属性、事件等,并将它们转换为源代码形式...

    dotnet反编译源码 NetDasm

    "dotnet反编译源码 NetDasm"是这个生态中的一个工具,它专注于将.NET的中间语言(IL,Intermediate Language)转换回可读的C#或其他.NET语言的源代码。NetDasm作为一个开源项目,允许开发者深入学习.NET的内部工作...

    dotNet反编译器

    3. Reflector.exe:这是dotNet反编译器的主要可执行文件,负责解析.NET程序集并展示其源代码。双击运行此文件,用户就可以开始反编译.NET组件。 4. ReadMe.htm:这是包含软件使用说明、版本信息或者开发者信息的...

    微软Dotnet反编译工具 ILDASM 4.0 修改版

    Protected Module是.NET Framework提供的一种安全特性,用于防止恶意用户逆向工程程序集,但ILDASM作为官方提供的反编译器,拥有合法访问这些受保护代码的能力。 【标签】:“Dotnet反编译工具”表明了讨论的核心是...

    dotnet core 应用是如何跑起来的 通过自己写一个 dotnet host 理解运行过程.rar

    在.NET Core中,一个应用程序的启动过程涉及到多个组件和步骤,包括托管环境(.NET Core runtime,也称为.dotnet host)的初始化、程序集加载、入口点查找以及代码执行等。这里我们将深入探讨如何通过编写自己的....

    dotnet资源打包

    1. **Reflector**: 这是一款著名的.NET反编译器,允许开发者查看.NET程序集的内部结构,包括类、方法、属性和资源。Reflector.exe是其执行文件,Reflector.exe.config则是配置文件,用于定制程序的行为。通过...

    .net脱壳机(Dotnet Dumper)

    为了保护代码不被轻易反编译,.NET程序集通常会被加上一层壳,即Strong Name签名或使用混淆器进行混淆。脱壳机的作用就是剥离这层壳,揭示底层的IL代码和元数据。 Dotnet Dumper相较于一般的.NET脱壳机,其独特之...

    dotnet-使用dnlib的NETPatcher补丁包

    1. **加载原程序集和补丁**:运行.NET Patcher时,先加载原始程序集,然后加载补丁文件,补丁文件可以是直接包含修改后IL的程序集,也可以是描述修改的元数据。 2. **解析补丁**:根据补丁文件的格式,解析出需要...

Global site tag (gtag.js) - Google Analytics