DotNet Assembly parsing and dotnet Parsing
In the. NET Framework, an assembly is the minimum unit for reuse, security, and version control. The Assembly is defined as a collection of one or more types of definition files and resource files. The Assembly mainly includes: PE/COFF, CLR header, metadata, list, pencil code, and metadata.
PE/COFF Files are generated by tools, indicating the logical grouping of files. The PE file contains a "list" data block. A list is another collection composed of metadata tables. These tables describe the files that constitute the assembly, types of public export implemented by files in the Assembly, and resources or data files associated with the Assembly.
Contains metadata and IL (an intermediate language of Microsoft) in a managed program. IL can access and manipulate object types, it also provides commands to create and initialize objects, call Virtual Methods on objects, and directly operate on array elements.
The CLR header is a small information block. It mainly contains the major (main) and major (secondary) versions of the target CLR, one MethodDef token (specifying the module entry method) and one optional strong-name digital signature.
Metadata indicates a binary data block, which consists of several tables: Definition table, reference table, and list table.
The above is a simple description of the composition of the Assembly. Next, let's take a look at some features of the Assembly: the Assembly defines reusable types, and the Assembly marks a version number; an assembly can have associated security information.
When running the program, the JIT compiler uses the TypeRef and AssemblyRef metadata tables of the Assembly to determine which Assembly defines the referenced type. During running, the JIT compiler needs to obtain information about the Assembly, including the name, version, language culture, and public key tag, and concatenate these information into a string. The JIT compiler will look for the Assembly with this identifier. If the Assembly is queried, the Assembly will be loaded to the AppDomain.
The following describes how to load an assembly in CLR:
In System. refection. the Static Load Method of the Assembly class is used to Load the Assembly. In the operation of loading the specified Assembly, the LoadFrom () method is used. LoadFrom () has multiple overloaded versions, let's take a look at the underlying implementation code of 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); }
In the operation of loading the Assembly, LoadFrom first calls Syatem. reflection. static Method of the AssemblyName class GetAssemblyName (this method opens the specified file, looks for the record items of the AssemblyRef meta-data table, extracts the Assembly ID information, and then uses a Syatem. reflection. returned information in the form of AssemblyName object). The LoadFrom method internally calls the Load method of Assembly and passes the AssemblyName object to it. CLR binds a Redirection policy to the application version, and find the matching assembly in each location. If Load finds a matched Assembly, it loads it and returns an Assembly object representing the loaded Assembly. The LoadFrom method returns this value.
Another method for program loading is LoadFile. This method can load an assembly from any path and load an assembly with the same ID to an AppDoamin multiple times. Next, let's take a look at the underlying implementation code of 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); }
The above is a simple description of the assembly structure and assembly loading method. It must be noted that the Assembly does not provide the unload function.
The following provides several common assembly operation methods:
1. Public attributes and methods:
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> // The cache time. The value 0 indicates that the cache is not cached (default value: 0 seconds, unit: seconds) /// </summary> public ReflectionSugar (int time = 0) {_ time = time ;} /// <summary> /// obtain the key based on the assembly path and name /// </summary> /// <param name = "keyElementArray"> </param> // /<returns> </Returns> private string GetKey (params string [] keyElementArray) {return string. join ("", keyElementArray );} /// <summary> /// check whether the key exists /// </summary> /// <param name = "key"> key </param> /// <returns> <c> true </c> does not exist <c> false </c>. </returns> private bool ContainsKey (string key) {return HttpRuntime. cache [key]! = Null;} // <summary> // obtain the Cache based on the key // </summary> private V Get <V> (string key) {return (V) httpRuntime. cache [key] ;}/// <summary> /// insert Cache. /// </summary> /// <param name = "key"> key </param> /// <param name = "value"> value </param>/ // <param name = "cacheDurationInSeconds"> expiration time, in seconds </param> // <param name = "priority"> cache item attribute </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. Load the Assembly:
/// <Summary> /// load the Assembly /// </summary> /// <param name = "path"> assembly 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. collection type based on the Assembly:
/// <Summary> /// obtain the type based on the Assembly /// </summary> /// <param name = "asm"> Assembly object </param> /// <param name = "nameSpace"> nameSpace </param> /// <param name = "className"> class name </param> /// <returns> assembly type </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. Create an object instance:
/// <Summary> /// create an object instance /// </summary> /// <typeparam name = "T"> </typeparam> /// <param name = "fullName"> namespace. type name </param> /// <param name = "assemblyName"> assembly (dll name) </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) ;}// namespace. type name, assembly var path = fullName + "," + assemblyName; // loading Type var o = Type. getType (path); // create an instance var obj = Activator Based on the type. createInstance (o, true); var reval = (T) obj; if (IsCache) Add <T> (key, reval, _ time ); // type conversion and return reval ;}
In the preceding method, after an object is created based on the loaded assembly, the returned value structure is added to the cache.