Introduction
With some of the work that I have been doing recently, I have had to spend lots of time inspecting and searching the GAC for certain assemblies. Some of the tools that I have found on the web to allow the GAC to be searched have been a little bit slow and basic for my liking, so to ease my development I have written a basic API to allow the GAC to be inspected, and a basic application that also aids in this task.
In this article, I will show how the GAC Manager tool works, how the API can be used, and the future of this project.
The GAC Manager Project
As I am hoping to get feedback on this project and improve upon it over time, I have created a CodePlex page for the project, where you can get the very latest source code and binaries, as well as submit feature requests and bugs. The project is hosted at:
https://gacmanager.codeplex.com/
Please do visit the site for more information and to make feature requests!
The GAC Manager Tool
The GAC Manager Tool can be downloaded and installed from the links at the top of the article or the CodePlex site. When the tool is installed, it creates a shortcut in the start menu, in a folder named 'GAC Manager'. Run the tool and you get an interface like this:
I have tried to keep the interface as lean as possible. The toolbar allows you to perform the following commands:
Refresh
As you may expect, this command refreshes the list of assemblies installed in the GAC.
Install Assembly
This command allows you to install an assembly into the GAC. Note that the assembly must be strongly named!
Help
This command takes you straight to the CodePlex website, where you can find more information on the application or submit bugs or feature requests.
Assembly Details
This command shows the assembly details window for the currently selected assembly:
Copy Display Name
This command copies the Display Name of the selected assembly to the clipboard. The Display Name is the qualified name of the assembly (including the version, public key etc) that can be used to load reflection details etc.
File Properties
The File Properties command simply invokes the shell 'File Properties' window for the selected assembly.
Uninstall Assembly
Uninstalls the selected assembly from the GAC.
Open Containing Folder
The command opens the folder that the assembly is located in.
Search
The search box simply lets you filter the display of the assemblies by searching through them. The assemblies that are displayed will be any assembly with the search string in the name OR the public key token.
The GAC Manager Api
All of the functionality required to enumerate assemblies, install, uninstall etc is provided by the GAC Manager Api class library. This library currently uses the Fusion API to discover details of the assemblies in the GAC.
Enumerating Assemblies
We can use the AssemblyCacheEnumerator object to enumerate the assemblies in the global assembly cache. An example is below:
var assemblyEnumerator = new AssemblyCacheEnumerator();
var assemblyName = assemblyEnumerator.GetNextAssembly();
while(assemblyName != null)
{
var assemblyDescription = new AssemblyDescription(assemblyName);
System.Diagnostics.Trace.WriteLine("Display Name: " + assemblyDescription.DisplayName);
assemblyName = assemblyEnumerator.GetNextAssembly();
}
As with many of the low-level API functions, the IAssemblyName object is what is actually passed around. This is a COM interface, and on its own not very useful. However, if we construct an 'AssemblyDescription' object from the interface, then we have access to a lot more information. Here's how the AssemblyDescription object looks:
An AssemblyDescription can be created from an IAssemblyName, or even from a plain string, as long as that string contains a valid assembly display name.
Installing an Assembly
The AssemblyCache class can be used to install an assembly.
var path = @"c:/MyAssembly.dll";
AssemblyCache.InstallAssembly(path, null, AssemblyCommitFlags.Default);
Easy! The second parameter is an Install Reference. This is an advanced topic and outside of the scope of this article, but it basically allows you to associate one assembly with another, or with an installed product. This means that if someone tries to uninstall that assembly later, it will not work unless the associated product is uninstalled at the same time.
Uninstalling an Assembly
The AssemblyCache class can be used to uninstall an assembly.
var displayName = @"Apex, Version=1.4.0.0, Culture=neutral, PublicKeyToken=98d06957926c086d, processorArchitecture=MSIL";
var uninstallDisposition = IASSEMBLYCACHE_UNINSTALL_DISPOSITION.Unknown;
try
{
AssemblyCache.UninstallAssembly(displayName, null, out uninstallDisposition);
}
catch (Exception exception)
{
throw new InvalidOperationException("Failed to uninstall the assembly.", exception);
}
if (uninstallDisposition == IASSEMBLYCACHE_UNINSTALL_DISPOSITION.IASSEMBLYCACHE_UNINSTALL_DISPOSITION_UNINSTALLED)
{
}
Uninstalling an assembly is more complicated. There are more things that can go wrong, an in fact the Uninstall Disposition enumeration is very helpful at letting us see what may have occurred. The second parameter for the UninstallAssembly function is an Install Reference - again as mentioned in the previous section this is a more complicated topic, but essentially it allows us to specify that the assembly is part of a larger collection of components.
Getting Advanced Fusion Properties
The AssemblyDescription object has a property named 'FusionProperties'. This class holds more advanced properties that can only be acquired via the Fusion API. The reason that they are stored in their own object is that they are loaded lazily - Fusion Properties will only be discovered if they are needed, to ensure that the initial loading of lots of assemblies is speedy. Here's how they can be used:
var someAssembly = new AssemblyDescription(new AssemblyCacheEnumerator().GetNextAssembly());
foreach(var installReference in someAssembly.FusionProperties.InstallReferences)
System.Diagnostics.Trace.WriteLine(installReference.Description);
Here you can see that we can access advanced details like the install references via the Fusion Properties.
Getting Advanced Reflection Properties
There is another object available with more properties for an AssemblyDescription, this is the ReflecitonProperties object. Again, this is lazy loaded - and with very good reason, getting properties of an assembly via Reflection can be very slow - so by keeping these properties separate we make it very clear in code that we're using a property that may have a heavy overhead. Here's how we can get them:
var someAssembly = new AssemblyDescription(new AssemblyCacheEnumerator().GetNextAssembly());
System.Diagnostics.Trace.WriteLine(someAssembly.ReflectionProperties.RuntimeVersio
And that's all there is to it!
The Future
Currently this tool does what I need it to, but I'd love to improve upon it and make it a useful tool for the community. If you have any suggestions, then please do comment either on this article or the project homepage!