文章出處

CLR中的程序集加載

      本次來討論一下基于.net平臺的CLR中的程序集加載的機制:

  【注:由于.net已經開源,可利用vs2015查看c#源碼的具體實現】

在運行時,JIT編譯器利用程序集的TypeRef和AssemblyRef元數據表來確定哪一個程序集定義了所引用的類型。在AssemblyRef元數據表的記錄項中,包含構成程序集的強名稱的各個部分。JIT編譯器獲取包括名稱(無擴展名和路徑)、版本、語言文化和公鑰標記,將這些連接成一個字符串。JIT編譯器將該標識匹配的一個程序集加載到AppDomain中。】

     CLR內部加載程序集提供了4中方法,在System.Refleetion.Assembly類中:

    1.     采用靜態方法Load()加載程序集,可調用它顯示的將一個程序集加載到AppDomain中:

           【注:Assembly類的Load()存在兩個重載版本】

   

 /// <summary>

    /// 通過給定的程序集的顯示名稱來加載程序集,使用提供的證據將程序集加載到調用方的域中。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="assemblyString">程序集的顯示名稱。</param>

    <param name="assemblySecurity">用于加載程序集的證據。</param>

  <exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 為 null。</exception>

  <exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception>

  <exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。-或 -當前加載的是 2.0 或更高版本的公共語言運行時,而<paramref name="assemblyString"/> 是用更高版本的公共語言運行時編譯的。</exception>

  <exception cref="T:System.IO.FileLoadException">發現一個未能加載的文件。- 或 -用兩個不同的證據將一個程序集或模塊加載了兩次。</exception>

  <PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet>

        [SecuritySafeCritical]

       [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of Load which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")]

    [MethodImpl(MethodImplOptions.NoInlining)]

    public static Assembly Load(string assemblyString, Evidence assemblySecurity)

    {

      StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

      return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, assemblySecurity, ref stackMark, false);

}

     /// <summary>

    /// 通過給定程序集的長格式名稱加載程序集。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="assemblyString">程序集名稱的長格式。</param>

<exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 為 null。</exception>

<exception cref="T:System.ArgumentException"><paramref name="assemblyString"/> 是零長度字符串。</exception>

<exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception>

<exception cref="T:System.IO.FileLoadException">發現一個未能加載的文件。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而<paramref name="assemblyString"/> 是用更高版本的公共語言運行時編譯的。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet>

    [SecuritySafeCritical]

    [__DynamicallyInvokable]

    [MethodImpl(MethodImplOptions.NoInlining)]

    public static Assembly Load(string assemblyString)

    {

      StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

      return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, false);

    }

 

      在內部,Load導致CLR向程序集應用一個版本綁定重定向策略,并在GAC(全局程序集緩存)中查找程序集。如果沒有找到,就接著去應用程序的基目錄、私有路徑目錄和codebase位置查找。如果Load找到指定的程序集,會返回對代表已加載的那個程序集的一個Assembly對象的引用。如果沒有找到,則會拋出一個異常。

  【注:System.AppDomain提供了一個Load方法,這與Assembly的靜態Load方法不同,AppDoamin的Load是一個實例方法,它允許將一個程序集加載到一個指定的AppDoamin中,該方法設計供非托管代碼調用,語序宿主將一個程序集“注入”一個特定的AppDoamin。】

 

2.采用Assembly的LoadFrom方法,指定路徑名的方式加載程序集:

   

   /// <summary>

    /// 已知程序集的文件名或路徑,加載程序集。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="assemblyFile">包含程序集清單的文件的名稱或路徑。</param>

<exception cref="T:System.ArgumentNullException"><paramref name="assemblyFile"/> 為 null。</exception>

<exception cref="T:System.IO.FileNotFoundException">未找到<paramref name="assemblyFile"/>,或者嘗試加載的模塊沒有指定文件擴展名。</exception>

<exception cref="T:System.IO.FileLoadException">發現一個未能加載的文件。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="assemblyFile"/> 不是有效的程序集;例如,64 位進程中的 32 位程序集。有關更多信息,請參見異常主題。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而<paramref name="assemblyFile"/> 是用更高版本的公共語言運行時編譯的。</exception>

<exception cref="T:System.Security.SecurityException">在沒有所需<see cref="T:System.Net.WebPermission"/> 的情況下,指定了不以“file://”開始的基本代碼。</exception>

<exception cref="T:System.ArgumentException"><paramref name="assemblyFile"/> 參數是空字符串 ("")。</exception>

<exception cref="T:System.IO.PathTooLongException">程序集名稱的長度大于 MAX_PATH 個字符。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet>

    [SecuritySafeCritical]

    [MethodImpl(MethodImplOptions.NoInlining)]

    public static Assembly LoadFrom(string assemblyFile)

    {

      StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

      return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, false, false, ref stackMark);

}

 

  (1).在內部,LoadFrom首先會調用Syatem.Reflection.AssemblyName類的靜態方法GetAssemblyName。該方法打開指定的文件,查找AssemblyRef元數據表的記錄項,提取程序集標識信息。

(2).以一個AssembleName對象的形式返回這些信息。

(3).LoadFrom方法內部調用Assembly的Load方法,將Assembly對象傳遞給他。

(4).CLR會為應用版本綁定重定向策略,并在各個位置查找匹配的程序集。

 

  3.采用Assembly的LoadFile方法,這個方法可以從任意路徑加載一個程序集,并可將具有相同標識的一個程序集多次加載到一個AppDoamin中。

   

 /// <summary>

    /// 加載指定路徑上的程序集文件的內容。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="path">要加載的文件的完全限定路徑。</param>

<exception cref="T:System.ArgumentException"><paramref name="path"/> 參數不是絕對路徑。</exception>

<exception cref="T:System.ArgumentNullException"><paramref name="path"/> 參數為 null。</exception><exception cref="T:System.IO.FileLoadException">發現一個未能加載的文件。</exception>

<exception cref="T:System.IO.FileNotFoundException"><paramref name="path"/> 參數為空字符串 ("") 或不存在。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="path"/> 不是有效程序集。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而

<paramref name="path"/> 是用更高版本的公共語言運行時編譯的。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>

<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="ControlEvidence"/></PermissionSet>

    [SecuritySafeCritical]

    public static Assembly LoadFile(string path)

    {

      AppDomain.CheckLoadFileSupported();

      new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand();

      return (Assembly) RuntimeAssembly.nLoadFile(path, (Evidence) null);

    }

 

    /// <summary>

    /// 通過給定的程序集的路徑來加載程序集,使用提供的證據將程序集加載到調用方的域中。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="path">程序集文件的完全限定路徑。</param>

<param name="securityEvidence">用于加載程序集的證據。</param>

<exception cref="T:System.ArgumentException"><paramref name="path"/> 參數不是絕對路徑。</exception>

<exception cref="T:System.ArgumentNullException"><paramref name="path"/> 參數為 null。</exception>

<exception cref="T:System.IO.FileNotFoundException"><paramref name="path"/> 參數為空字符串 ("") 或不存在。</exception>

<exception cref="T:System.IO.FileLoadException">發現一個未能加載的文件。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="path"/> 不是有效程序集。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而

<paramref name="path"/> 是用更高版本的公共語言運行時編譯的。</exception>

<exception cref="T:System.NotSupportedException"><paramref name="securityEvidence"/> 不是 null。默認情況下,舊的 CAS 策略中未啟用 .NET Framework 4; 如果未啟用), <paramref name="securityEvidence"/> 必須是 null。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>

<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="ControlEvidence"/></PermissionSet>

    [SecuritySafeCritical]

    [Obsolete("This method is obsolete and will be removed in a future release of the .NET Framework. Please use an overload of LoadFile which does not take an Evidence parameter. See http://go.microsoft.com/fwlink/?LinkID=155570 for more information.")]

    [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlEvidence)]

    public static Assembly LoadFile(string path, Evidence securityEvidence)

    {

      AppDomain.CheckLoadFileSupported();

      if (securityEvidence != null && !AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled)

        throw new NotSupportedException(Environment.GetResourceString("NotSupported_RequiresCasPolicyImplicit"));

      new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, path).Demand();

      return (Assembly) RuntimeAssembly.nLoadFile(path, securityEvidence);

    }

 

  通過LoadFile加載程序集時,CLR不會自動解析任何依賴性問題,代碼必須向AppDomain的AssemblyReaolve事件登記,并讓事件回調方法顯示的加載依賴的程序集。

 

     4.如果需要構建的一個工具只是通過反射來分析程序集的元數據,并希望確保程序集中的任何代碼都不會執行,那么程序集的最佳方式就是使用Assembly的ReflectionOnlyLoadFrom方法或者使用ReflectionOnlyLoad方法。

  

  /// <summary>

    /// 將給定顯示名稱的程序集加載到只反射上下文中。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

    /// <param name="assemblyString">程序集的顯示名稱,由

<see cref="P:System.Reflection.AssemblyName.FullName"/> 屬性返回。</param>

<exception cref="T:System.ArgumentNullException"><paramref name="assemblyString"/> 為 null。</exception>

<exception cref="T:System.ArgumentException"><paramref name="assemblyString"/> 為空字符串 ("")。</exception>

<exception cref="T:System.IO.FileNotFoundException"><paramref name="assemblyString"/> 未找到。</exception>

<exception cref="T:System.IO.FileLoadException"><paramref name="assemblyString"/> 已找到,但是不能加載。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="assemblyString"/> 不是有效程序集。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而<paramref name="assemblyString"/> 是用更高版本的公共語言運行時編譯的。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet>

    [SecuritySafeCritical]

    [MethodImpl(MethodImplOptions.NoInlining)]

    public static Assembly ReflectionOnlyLoad(string assemblyString)

    {

      StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

      return (Assembly) RuntimeAssembly.InternalLoad(assemblyString, (Evidence) null, ref stackMark, true);

}

    /// <summary>

    /// 將給定路徑的程序集加載到只反射上下文中。

    /// </summary>

    ///

    /// <returns>

    /// 加載的程序集。

    /// </returns>

/// <param name="assemblyFile">包含程序集清單的文件的路徑。</param>

<exception cref="T:System.ArgumentNullException"><paramref name="assemblyFile"/> 為 null。</exception>

<exception cref="T:System.IO.FileNotFoundException">未找到

<paramref name="assemblyFile"/>,或者嘗試加載的模塊沒有指定文件擴展名。</exception>

<exception cref="T:System.IO.FileLoadException"><paramref name="assemblyFile"/> 已找到,但是未能加載。</exception>

<exception cref="T:System.BadImageFormatException"><paramref name="assemblyFile"/> 不是有效程序集。- 或 -當前加載的是 2.0 或更高版本的公共語言運行時,而<paramref name="assemblyFile"/> 是用更高版本的公共語言運行時編譯的。</exception>

<exception cref="T:System.Security.SecurityException">在沒有所需 <see cref="T:System.Net.WebPermission"/> 的情況下,指定了不以“file://”開始的基本代碼。</exception>

<exception cref="T:System.IO.PathTooLongException">程序集名稱的長度大于 MAX_PATH 個字符。</exception>

<exception cref="T:System.ArgumentException"><paramref name="assemblyFile"/> 為空字符串 ("")。</exception>

<PermissionSet><IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/></PermissionSet>

    [SecuritySafeCritical]

    [MethodImpl(MethodImplOptions.NoInlining)]

    public static Assembly ReflectionOnlyLoadFrom(string assemblyFile)

    {

      StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;

      return (Assembly) RuntimeAssembly.InternalLoadFrom(assemblyFile, (Evidence) null, (byte[]) null, AssemblyHashAlgorithm.None, true, false, ref stackMark);

    }

 

 

     ReflectionOnlyLoadFrom方法加載有路徑指定的文件,文件的強名稱標識不會獲取,也不會在GAC和其他位置搜索文件。ReflectionOnlyLoad方法會在GAC、應用程序基目錄、私有路徑和codebase指定的位置搜索指定的程序集,該方法不會應用版本控制策略,因此在指定的是那個版本,獲取的就是那個版本。如果要自行為一個程序集標識指定版本控制策略,可將字符串傳給AppDoamin的ApplyPolicy方法。

     用ReflectionOnlyLoadFrom或ReflectionOnlyLoad方法加載程序集時,CLR禁止程序集中的任何代碼執行,如果試圖執行,則會拋出異常。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()