7

Is it possible to enumerate all installed versions of an assembly in GAC using C#? For example I have the assembly named "My.Assembly". The assembly may come in various versions ("1.0.0.0", "2.3.4.5", "0.1.2.4", ...) and may be compiled for various platforms (x86, x64, Any CPU).

Now I need a way to determine which of the versions/platforms are installed.

I'm aware that I could enumerate the directories in GAC but that seems wrong. There should be a better way to do this.

Background I have a launcher application in which the user selects a DLL. The launcher retrieves some information from the DLL (without loading it) and then has to start the correct managed C# application which handles the DLL. The DLL may be compiled for Win32 or x64 put exposes always the same (platform independent) interface. I use the LoadLibrary function to load the DLL in the C# applicaiton. The only problem is that the process has to be of matching format (x86 or x64). The C# application can and should be compiled for x86, x64 and Any CPU.

Korexio
  • 483
  • 1
  • 7
  • 19
  • 1
    You do this with the Fusion api, CreateAssemblyEnum() and IAssemblyEnum + IAssemblyName. This is not exposed in the .NET framework but it *does* contain the code for it. ILSpy or Reflector will be handy. – Hans Passant Jun 15 '12 at 13:28
  • @HansPassant Fusions seems to be exactly what I was looking for. It seems hard to use though. Would be nice if there was managed/builtin support for this kind of thing. Thanks a lot! – Korexio Jun 18 '12 at 06:18

1 Answers1

7

Using a managed wrapper for the Unmanaged Fusion API a was able to do exactly what I wanted to do:

class Program
{

    static IEnumerable<AssemblyName> GetInstalledVersions(string name)
    {
        int result;

        IAssemblyName assemblyName;
        result = Utils.CreateAssemblyNameObject(out assemblyName, name, CreateAssemblyNameObjectFlags.CANOF_DEFAULT, IntPtr.Zero);
        if ((result != 0) || (assemblyName == null))
            throw new Exception("CreateAssemblyNameObject failed.");

        IAssemblyEnum enumerator;
        result = Utils.CreateAssemblyEnum(out enumerator, IntPtr.Zero, assemblyName, AssemblyCacheFlags.GAC, IntPtr.Zero);
        if ((result != 0) || (enumerator == null))
            throw new Exception("CreateAssemblyEnum failed.");

        while ((enumerator.GetNextAssembly(IntPtr.Zero, out assemblyName, 0) == 0) && (assemblyName != null))
        {
            StringBuilder displayName = new StringBuilder(1024);
            int displayNameLength = displayName.Capacity;
            assemblyName.GetDisplayName(displayName, ref displayNameLength, (int)AssemblyNameDisplayFlags.ALL);
            yield return new AssemblyName(displayName.ToString());
        }

    }

    static void Main(string[] args)
    {
        foreach (AssemblyName assemblyName in GetInstalledVersions("System.Data"))
            Console.WriteLine("{0} V{1}, {2}", 
                assemblyName.Name, assemblyName.Version.ToString(), assemblyName.ProcessorArchitecture);
    }
}

Running the program above gives me the following output:

System.Data V2.0.0.0, Amd64
System.Data V2.0.0.0, X86
System.Data V4.0.0.0, Amd64
System.Data V4.0.0.0, X86

Thanks to Hans Passant who pointed me in the right direction!

Korexio
  • 483
  • 1
  • 7
  • 19
  • Side note: If the fusion wrapper is compiled for Framework 2.0, it won't return results that exist in global assembly cache for CLR version 4. – Scott Solmer Sep 01 '16 at 14:50
  • Wrapper blog link rotted, available at http://web.archive.org/web/20160224040605/http://blogs.msdn.com/b/junfeng/archive/2004/09/14/229649.aspx (maybe it was migrated to a new location) – Kim Sullivan Feb 14 '22 at 16:43