Building the AssemblyValidator
The first step in validating the currently executing assembly's dependencies is to retrieve a handle to the currently executing assembly, because you need a handle to the topmost assembly for the process in which the validation code is running. The System.Reflection namespace's Assembly class provides the
GetEntryAssembly() function that you can use to obtain a handle to this assembly. The function returns an Assembly object, which represents an assembly that has been loaded into memory.
Dim objAssembly_Self As Assembly
ObjAssembly_Self = Assembly.GetEntryAssembly()
For reference, the Assembly class provides access to the metadata exposed by a
particular instance of an assembly. It is important to note that the Assembly class is tied to an instance of an assembly loaded into memory because it is possible, especially with the Xcopy deployment methods promulgated by Microsoft, to have many identical assemblies that differ only by their locations. If you are interested in only the generic information about an assembly, use the AssemblyName class.
The AssemblyName class stores enough information about an assembly to enable you to load an instance into memorymore specifically, it provides enough information for the .NET Framework to find and load it for you. One key detail used by .NET is the assembly's
FullName property, which holds an assembly's name, version, culture, and public key. This combination of attributes ensures that .NET loads the exact assembly you intendno two assemblies should ever have an identical
FullName. When you query the assembly metadata for referenced assemblies the Assembly class returns a list of referenced assemblies as AssemblyName objects.
So, after you get a reference to the entry assembly, you can request the list of assemblies it references, returned as an array of AssemblyName objects. You can then iterate through the array, passing each AssemblyName object to a recursive method named
ValidateAssembly. The recursive nature of this function ensures that the AssemblyValidator validates all the dependencies that exist in the hierarchical assembly dependency structure.
Dim objDepAssembly As AssemblyName
For Each objDepAssembly in _
objAssembly_Self.GetReferencedAssemblies()
ValidateAssembly(objDepAssembly)
Next
Internally, the
ValidateAssembly method uses an Assembly_List object defined in the validation tool to keep track of which assemblies have been referenced and to maintain details about each of the referenced assemblies. Other than keeping track of assembly details, the most important role of the Assembly_List object is to avoid repeatedly validating assemblies that have already been validated. Worse than the small amount of additional time required to re-verify assemblies, you could quickly cause a stack overflow if the recursive calls exhausted your application's memory resources. So, before adding another assembly to the Assembly_List object, the AssemblyValidator first checks the list to see if it's already been verified. If so (it exists in the list), the tool stops the recursion and returns from the
ValidateAssembly method.
The
ValidateAssembly method first attempts to load the assembly using the AssemblyName object provided as a parameter. The Assembly class provides a shared overloaded
Load method; the sample application uses the overload version that accepts an assembly name object. The
CreateAssembly method shown below demonstrates how to use the AssemblyName object to load assemblies. Note the possible common exceptions raised by the
Load method.
'attempt to create the assembly using the assembly name object
Private Function CreateAssembly( _
ByVal p_objAssemblyName As AssemblyName, _
ByRef p_strError As String) As Assembly
Dim objAssembly As System.Reflection.Assembly
'---- try to create the assembly
Try
objAssembly = System.Reflection.Assembly.Load( _
p_objAssemblyName)
p_strError = ""
Catch exSystem_BadImageFormatException As _
System.BadImageFormatException
p_strError = "File is not a .NET assembly"
objAssembly = Nothing
Catch exSystem_IO_FileNotFoundException As _
System.IO.FileNotFoundException
p_strError = "Could not load assembly -- " & _
"file not found"
objAssembly = Nothing
Catch ex As Exception
p_strError = "An error occurred loading the assembly"
objAssembly = Nothing
End Try
Return objAssembly
End Function
If the assembly cannot be loaded, then recursion stops at this level, and the AssemblyValidator logs an error in the Assembly_List indicating why the assembly could not be loaded. When the assembly loads successfully, the AssemblyValidator adds the assembly details to the Assembly_List object, and recursively verifies each of this assembly's referenced assemblies. This process continues until all the dependencies have been verified.
Listing 1 shows the complete
ValidateAssembly method.
At the end of the process, the Assembly_List class provides a
FormatList method used to produce a string representation of the list of referenced assemblies. By default, the AssemblyValidator displays only assemblies that could not be loaded, because it's far too difficult to scroll through the lists of dependencies manually, looking for problemseven simple "Hello World" WinForms projects produce long lists of dependencies.
As an exercise, I recommend that you modify the sample code to instruct the Assembly_List to display all dependencies (without duplicates), including those assemblies that loaded successfully, and observe the list that is produced. To make this modification, open the Assembly_Validator class in the AssemblyDependencyValidator project, and modify the last line of the
ValidateEntryAssembly method, changing the parameter to the
FormatList method to be
False, as shown below:
m_strResults = strValidatorResults & vbCrLf & _
m_objBindingInfo.FormatList(False)