For most production SharePoint deployments that involve custom .NET code, such as custom developed web parts, controls, event handlers, etc., best practice dictates that any shared assemblies should be deployed to the Global Assembly Cache (GAC). While this is a great practice, the GAC location (e.g. %WINDIR%\Assembly) does not behave like a typical folder, or at least doesn’t appear to, and for most .NET developers, the GAC in general is somewhat of a mystery. One of the biggest perplexities with the GAC is that upon first glace, there does not seem to be a means for retrieving a copy of an assembly that is deployed to the GAC. While all of us have probably dragged a .NET assembly into the %WINDIR%\Assembly folder to add it to the GAC, if you try dragging an item out of the GAC, it does nothing, and the familiar Cut/Copy/Paste operations that are standard on any Windows Explorer folder view are grayed-out. How dare you do this to us Microsoft?!
In the sections below I will explain how you can retrieve the actual DLL for an assembly that is only deployed within the GAC, and in the process, we will make some sense out of how the GAC actually works and see how it really is just a specialized folder hierarchy for storing .NET assemblies.
The Uniqueness of the %WINDIR%\Assembly Path Location
If you open a Windows Explorer to the %WINDIR%\Assembly path location, you are presented with a specialized view of this folder, as shown below:
This unique view is the result of a Windows Shell extension called SHFusion.dll (as an aside, the reason for this name is the original codename for the GAC, when the .NET Framework was first being developed, was Fusion). However, if you open a Command Prompt and switch to the %WINDIR%\Assembly path, you see a slightly different listing:
The reason for the Command Prompt displaying this view, as opposed to what Windows Explorer shows, is that Windows Shell extensions are not applied to file operations performed from the Command Prompt. Here we are presented with seven (7) folders, each of which is explained below:
- GAC – stores .NET 1.x assemblies
- GAC_32 – stores .NET 2.x+ assemblies that are built specifically for 32-bit architecture.
- GAC_MSIL – stores .NET 2.x+ assemblies that are architecture independent, meaning they can be used on 32- or 64-bit systems.
- NativeImages_Vx_Architecture – there can be multiple folders of this format, specific to each version of the .NET Framework present on the system, with possible 32 and 64 bit flavors of each. Each of these folders is used for storing native images of .NET assemblies that have been pre-compiled using NGen, to eliminate the need to run the MSIL code through the JIT compiler at execution time.
- temp, tmp – temporary folders.
Additionally, on a 64-bit system, one additional folder would be present:
- GAC_64 – stores .NET 2.x+ assemblies that are built specifically for 64-bit architecture.
To determine which folder the assembly you seek is within (e.g. GAC, GAC_32, or GAC_64), refer to the Processor Architecture column within the Fusion-based Windows Explorer view. Let us consider an example:
Assume we would like to retrieve the DLL for the Contoso.Website.Controls assembly, shown in the Explorer view above. As we can see, the Processer Architecture indicated is MSIL, indicating that this assembly will be within the GAC_MSIL subfolder, if we proceed to descend into that level, we see the following:
At this level, we have a subfolder for each assembly, so let us descend into the Contoso.Website.Controls subfolder, to see the following:
At this level, we see folders with names based upon the version of the assembly, culture (if not invariant), and public key token. It is this exact folder structure the enables the GAC to store multiple versions of the same assembly without one overwriting or conflicting with the other. Since there is only one subfolder present at this level, let us proceed to descend into it, to see the following:
Voila! At this level, we are able to take a copy of the exact DLL file that was deployed to the GAC. What was a pleasant surprise to me is that the GAC does not in any way mangle the original name of the DLL that was deployed.
Disabling the SHFusion.dll Shell Extension
There is another option of seeing the actual folder structure of the GAC that does not involve the command-line, but I would not recommend doing this on a production system. To view the same folder structure within Windows Explorer that we were able to navigate through using the Command Prompt, we can disable the SHFusion.dll shell extension using one of three possible methods:
- Rename %WINDIR%\Assembly\Desktop.ini – within %WINDIR%\Assembly, a hidden, read-only, system attributed file named Desktop.ini is present. By renaming this file to something else, such as Desktop.ini.bak, we effectively prevent SHFusion.dll from loading for this folder.
- Add the DisableCacheViewer Registry Value – within the HKLM\Software\Microsoft\Fusion registry path, a new DWORD value can be created with the name DisableCacheViewer and a value of 1 to prevent SHFusion.dll from being loaded.
- Rename SHFusion.dll – the SHFusion.dll itself can be found within %WINDIR%\Microsoft.NET\Framework\v.XX, and if this file is renamed, it will not be loaded when Windows Explorer is pointed at the %WINDIR%\Assembly path.
By performing any one of the above steps, upon opening %WINDIR%\Assembly the next time, you will be presented with the following view, similar to what we saw within the Command Prompt dir listing above:
So there you have it – the biggest mysteries of the GAC have all been dispelled: how its path location works within Windows, its unique rendering method within Windows Explorer, how it actually stores DLLs you deploy to it, and how you can retrieve them later. So in reality, all the GAC truly is made up of is a specialized folder hierarchy for tracking multiple versions of assemblies, independent of processor architecture, that has special .NET CAS policies defined for it to allow assemblies deployed to this location to have Full Trust permissions. Now go forth, fire up your compilers, and start deploying code to the GAC!