Using the .NET Fusion API to Manipulate the GAC

Developers who have used the wonders of the CLR and native code, whether via Managed C++, C++/CLI, or some other managed language, know the Global Assembly Cache (GAC) well. On a recent development project for a client, I needed to add an existing assembly to the GAC. The assembly, which shipped with Microsoft Office 2003, defined a custom code group that allowed shared drives on network servers to be trusted for documents that used Visual Studio Tools for Office (VSTO) functionality (the default security policy allows only local Office documents to use VSTO).

This MSDN document describes the custom code group and how to use it. It specifies two ways to add the Microsoft Office assembly to the GAC:

  • Using the .NET Configuration Utility
  • Using the GACUTIL Framework SDK tool

The .NET Configuration Utility requires administrative privileges to add an assembly to the GAC, and the GACUTIL is available only when the Framework SDK is installed. A machine with the .NET Framework but without the SDK won't have GACUTIL. Based on the advice in the MSDN article, a developer would have to make every potential user of a VSTO application into a local administrator (at least temporarily) or get the Framework SDK installed so GACUTIL can be called from an MSI package, which runs under the privileges of an administrator. Thankfully, the C++ developer has a third option: programmatically modifying the GAC with the Fusion APIs.

Fusion is the code name Microsoft has given the .NET Framework sub-system responsible for locating and loading assemblies. The GAC falls under Fusion's area of responsibility, and the GAC manipulation methods are exposed via COM interfaces implemented in fusion.dll. For those thinking that COM interop is generally easy because type libraries can be used to generate managed wrappers without the need to manually define interoperability method signatures, Fusion throws added complexity into the mix by shipping without a type library. The great Microsoft Fusion blogger Junfeng Zhang explains the absence of the type library in this post. Thankfully, Microsoft has documented the GAC functions and interfaces in KB Article 317540.

Dusting off one of the great command-line tools of the COM era, the IDL code from the KB article can be pasted into a local IDL file and compiled with the Microsoft IDL (MIDL) compiler to produce a C++ header file. This file then can be #included in any C++ project. To allow developers not lucky enough to be fluent in C++ to add assemblies to the GAC programmatically, a Managed C++ assembly will be developed to wrap and expose the Fusion APIs. Using Managed C++ rather than C++/CLI allows the wrapper to be used on systems with .NET 1.1 and .NET 2.0 installed.

Rather than use CoCreateInstance, the Fusion COM class implementing the GAC manipulation interface is created with a C-style DLL call to Fusion (the method signature for this function is defined in the KB article). The first tasks are to use a typedef to make the C-style function pointer that will be loaded from fusion.dll easier to use, and to define two global variables to hold the module handle and function pointer from Fusion:

typedef HRESULT (__stdcall *CreateAsmCache)(IAssemblyCache
   **ppAsmCache, DWORD dwReserved);

HMODULE g_FusionDll;
CreateAsmCache g_pfnCreateAssemblyCache;

These global variables can be populated from within a method that will be called from the exposed managed type's static constructor:

void InitFunctionPointers(){
   LoadLibraryShim(L"fusion.dll", 0, 0, &g_FusionDll);
   g_pfnCreateAssemblyCache =
      (CreateAsmCache)GetProcAddress(g_FusionDll,
          "CreateAssemblyCache");
}

All error conditions from the managed assembly should be expressed via exceptions. However, to allow the flexibility to communicate non-fatal issues in the future, a managed enum that currently has only a single value indicating success will be defined:

public __value enum InstallResult {
   OK,
};

After all the build-up, the managed type that exposes the ability to add an assembly to the GAC is quite unremarkable and simple:

public __gc class Installer
{
   public:
   static Installer(){
      InitFunctionPointers();
   }

   static InstallResult InstallAssembly(String* fileName){
      CComPtr<IAssemblyCache> pCache = NULL;
      HRESULT hr = g_pfnCreateAssemblyCache(&pCache, 0);

      if (hr == S_OK){
         const __wchar_t __pin * wFileName =
            PtrToStringChars(fileName);
         hr = pCache->InstallAssembly(0, wFileName, NULL);

         if (hr == S_OK){
            return InstallResult::OK;
      }
   }

   System::Runtime::InteropServices::Marshal::ThrowExceptionForHR(hr);
   }
};

A few points about the type are worth highlighting:

  • ATL smart-pointers are used to cut down the tedious AddRef and Release calls needed by COM.
  • The PtrToStringChars helper method defined in vcclr.h is used to convert the managed System::String to a C-style string.
  • The Marshal::ThrowExceptionForHR method is used to translate between the HRESULT COM world and the System::Exception .NET world.

C++ Saves Time and Effort

The entire source code file for the managed wrapper for installing an assembly in the GAC, including white space, typedefs, pre-processor directives, and production-ready error handling comes in at exactly fifty lines. Compared with the equivalent time and effort required to accomplish this task from managed code-only languages such as C# and Visual Basic .NET, C++ clearly comes out on top.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility. Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

  • Live Event Date: March 18, 2015 @ 2:00 p.m. ET / 11:00 a.m. PT Content is king, and businesses of all kinds want to leverage content to demonstrate their business benefits to customers, prospects, and partners alike. With new media options – 4K, 3D, HTML, and new devices – all becoming a part of your communications strategy, what tools are there that can speed time to value while reducing learning curve and duplication of efforts? Check out this upcoming eSeminar to learn how working with Adobe …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date