Fusion GAC API Samples


For displaying purpose, error handling is skipped in all the sample code. Please don’t skip error handling in production code.

1.1.                Get GAC API Interfaces

Before use GAC API interfaces, you have to get GAC API interfaces.

 

GAC API interfaces are pseudo COM like. You need to call AddRef() and Release() when appropriate. But you can not get the interfaces by calling CoCreateInstance(). Instead, fusion.dll provides several exports to get those interfaces.

 

There is no lib file provided for fusion’s exports. The reason is simple: fusion.dll lives in .net framework directory, which by default is not in path. If we provided a fusion.lib, and you used it, NT loader will not be able to load fusion.dll for you.

 

The right way to get fusion’s exports is to use mscoree!LoadLibraryShim to locate fusion.dll, then call GetProcAddress().

 

The following is a sample code to get fusion’s exports:

 

#include “mscoree.h”

#include “fusion.h”

 

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

 

typedef HRESULT (__stdcall *CreateAsmNameObj)(LPASSEMBLYNAME *ppAssemblyNameObj, LPCWSTR szAssemblyName, DWORD dwFlags, LPVOID pvReserved);

 

typedef HRESULT (__stdcall *CreateAsmEnum)(IAssemblyEnum **pEnum, IUnknown *pAppCtx, IAssemblyName *pName, DWORD dwFlags, LPVOID pvReserved);

 

HMODULE g_FusionDll = NULL;

CreateAsmCache g_pfnCreateAssemblyCache = NULL;

CreateAsmNameObj g_pfnCreateAssemblyNameObject = NULL;

CreateAsmEnum g_pfnCreateAssemblyEnum = NULL;

 

LoadLibraryShim(L"fusion.dll", 0, 0, &g_FusionDll);

g_pfnCreateAssemblyCache = (CreateAsmCache)GetProcAddress(g_FusionDll, CreateAssemblyCache");

g_pfnCreateAssemblyNameObject = (CreateAsmNameObj)GetProcAddress(g_FusionDll, “CreateAssemblyNameObject");

g_pfnCreateAssemblyEnum = (CreateAsmEnum)GetProcAddress(g_FusionDll, CreateAssemblyEnum");

 

You need to link against mscoree.lib, which is included in .Net framework SDK.

 

All the sample code below assumes you have initialized GAC API interfaces.

1.2.                Install Assembly without Trace Reference

It is recommended that you install an assembly with reference. But the sample code of installing assembly without reference is included for illustration purpose.

 

// Get an IAssemblyCache interface

IAssemblyCache*     pCache      = NULL;

g_pfnCreateAssemblyCache(&pCache, 0);

 

// call IAssemblyCache::InstallAssembly

HRESULT hr = pCache->InstallAssembly(0, pszAssemblyFilePath, NULL);

 

// Report result based on the return hr.

1.3.                Install Assembly with Trace Reference

// Create an FUSION_INSTALL_REFERENCE struct and fill it with data

FUSION_INSTALL_REFERENCE installReference;

Memset(&installReference, 0, sizeof(FUSION_INSTALL_REFERENCE);

installReference.cbSize= sizeof(FUSION_INSTALL_REFERENCE);

installReference.dwFlags=0;

// We use opaque scheme here

installReference.guidSchem = FUSION_REFCOUNT_OPAQUE_STRING_GUID;

installReference.szIdentifier = L”Your Application Identifier goes here”;

installReference.szNonCannonicalData= L”Informational Description goes here”;

 

// Get an IAssemblyCache interface

IAssemblyCache*     pCache   = NULL;

g_pfnCreateAssemblyCache(&pCache, 0);

 

// call IAssemblyCache::InstallAssembly with reference

HRESULT hr = pCache->InstallAssembly(0, pszAssemblyFilePath, &installReference);

 

pCache->Release();

// Report result based on the return hr.

1.4.                Uninstall Assembly with Trace Reference

// Create an FUSION_INSTALL_REFERENCE struct and fill it with data

FUSION_INSTALL_REFERENCE installReference   = NULL;

installReference.cbSize= sizeof(FUSION_INSTALL_REFERENCE);

installReference.dwFlags=0;

// We use opaque scheme here

installReference.guidSchem = FUSION_REFCOUNT_OPAQUE_STRING_GUID;

installReference.szIdentifier = L”Your Application Identifier goes here”;

installReference.szNonCannonicalData= L”Informational Description goes here”;

 

// Get an IAssemblyCache interface

IAssemblyCache*     pCache      = NULL;

g_pfnCreateAssemblyCache(&pCache, 0);

 

// call IAssemblyCache::UninstallAssembly with reference

 

// UninstallAssembly takes a fully specified (including processor architecture) assembly name as input.

LPWSTR pszAssemblyName = L”MyAssembly, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=0123456789abcdef, ProcessorArchitecture=MSIL”;

ULONG ulDisp = 0;

 

HRESULT hr = pCache->UninstallAssembly(0, pszAssemblyName, &installReference, &ulDisp);

 

pCache->Release();

 

// report result based on return hr and ulDisp.

1.5.                Query Assembly in GAC

// Create an ASSEMBLY_INFO struct and fill it with data

ASSEMBLY_INFO   info;

WCHAR   path[MAX_PATH];

 

memset(&info, 0, sizeof(ASSEMBLY_INFO);

info.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);

info.pszCurrentAssemblyPathBuf = path;

info.cchBuf = MAX_PATH;

 

// Get an IAssemblyCache interface

IAssemblyCache*     pCache      = NULL;

g_pfnCreateAssemblyCache(&pCache, 0);

 

// QueryAssemblyInfo takes a fully specified (including processor architecture) assembly name as input.

LPWSTR pszAssemblyName = L”MyAssembly, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=0123456789abcdef, ProcessorArchitecture=MSIL”;

 

HRESULT hr = pCache->QueryAssemblyInfo(0, pszAssemblyName, &info);

 

pCache->Release();

 

// path[] now contain the path of the assembly in GAC if QueryAssemblyInfo returns success

1.6.                Enumerate Assembly in GAC

// Enumerate GAC for all the assembly “System”.

// “System” can be any valid assembly display name.

// If you want to see all assemblies, set pNameFilter to NULL

 

LPWSTR pszAssemblyName = L”System”;

IAssemblyName*  pNameFilter = NULL;

IAssemblyEnum* pEnum = NULL;

IAssemblyName* pAsmName = NULL;

DWORD           dwDisplayFlags = ASM_DISPLAYF_VERSION

                        | ASM_DISPLAYF_CULTURE

                        | ASM_DISPLAYF_PUBLIC_KEY_TOKEN

                        | ASM_DISPLAYF_PROCESSORARCHITECTURE;

DWORD dwLen = 0;

LPWSTR          szDisplayName = NULL;

 

// First, create a filter for “system”

g_pfnCreateAssemblyNameObject(&pNameFilter, pszAssemblyName, CANOF_PARSE_DISPLAY_NAME, NULL);

 

// now create the IAssemblyEnum for GAC

g_pfnCreateAssemblyEnum(&pEnum, NULL, pNameFilter, ASM_CACHE_GAC,  NULL);

 

// Enumerating.

// GetNextAssembly return S_OK when there are still assemblies exist for the enum,

// and S_FALSE if there is nothing left.

While (pEnum->GetNextAssembly(NULL, &pAsmName, 0) == S_OK)

{

    // pAsmName is the assembly returned by the enum. Now let’s get its display name

    dwLen = 0;

 

    // get the size first.

    pAsmName->GetDisplayName(NULL, &dwLen, dwDisplayFlags);

    // allocate memory

    szDisplayName = new WCHAR[dwLen];

    // re-try

    pAsmName->GetDisplayName(szDisplayName, &dwLen, dwDisplayFlags);

    // show it

    printf(“%S”, szDisplayName);

    delete[] szDisplayName;

    pAsmName->Release();

}

 

pEnum->Release();

pNameFilter->Release();

Comments (8)

  1. Boris says:

    Hello,

    Thank you for examples. I did investigated how one can programmatically iterate through GAC and so far didn’t find any suitable solution. Could you clarify the following. As I undestand the IAssemblyEnum interface is undocumented interface and should not be used in applications. Since it is not documented it can be either modified or even dropped (theoretically) in the next versions of .NET framework. Could you comment?

    Regards,

    Boris

  2. Boris says:

    Yes I read this article before and I quote:

    #######################################

    CAUTION: Do not use these APIs in your application to perform assembly binds or to test for the presence of assemblies or other run time, development, or design-time operations. Only administrative tools and setup programs must use these APIs. If you use the GAC, this directly exposes your application to assembly binding fragility or may cause your application to work improperly on future versions of the .NET Framework.

    #######################################

    Seems to me that this article just describes undocumented feature which is definitely not recommended for general use. Can you comment?

    Regards,

    Boris

  3. The paragraph states: Don’t try to use the GAC API to implement an assembly bind system. It is meant to be used by administrator to manage GAC.

    The other warning is that do not attempt to use this API to find the assembly path in GAC, then assume it will always be the same. The contract to GAC is the assembly’s full identity. The path is implementation detail and will change from .Net framework version to from.

    It all boils down to how you plan to use GAC APIs. Let me have a separate blog on this.

  4. Yves Dolce says:

     

    This is a version that fulfill my needs but if you need filtering, the information required is…

  5. Yves Dolce says:

     

    This is a version that fulfills my needs but if you need filtering, the information required…

  6. Yves Dolce says:

     

    This is a version that fulfills my needs but if you need filtering, the information required…

  7. Yves Dolce says:

     

    This is a version that fulfills my needs but if you need filtering, the information required…