Give the .NET loader a hand - how to load assemblies from VS PrivateAssemblies

This program demonstrates how to use the AppDomain.AssemblyResolve event to help the .NET loader get assemblies loaded that it can't find.

The VS Team Foundation edition ships with a number of assemblies you may want to reference in your own apps. Most of the important ones are installed in the GAC so you should have no problem loading them at runtime. However, some of them are installed under the VS PrivateAssemblies directory (by default this is C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies). Your options are you can copy these DLLs around, put that directory in your path, put your tool in the same directory, or have .exe.config file for your app that tells the loader where to look. Quite frankly, none of these options is particularly appealing since they all make distributing your tool more cumbersome. So let's help the loader out!

 using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.Win32;
 
namespace TeamFoundationAssemblyLoadTest
{
    class Program
    {
        static void Main(string[] args)
        {
            AppDomain domain = AppDomain.CurrentDomain;
 
            domain.AssemblyResolve += new ResolveEventHandler(domain_AssemblyResolve);
 
            //  The following line triggers the AssemblyResolve event to fire because
            //  it uses a class in Microsoft.TeamFoundation.WorkItemTracking.Controls.dll
            //  which is not installed in the GAC or anywhere else convenient.
            ClassThatUsesTeamFoundation.SomeFunction();
        }
 
        static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            String[] assemblyParams = args.Name.Split(',');
            
            Debug.Assert(assemblyParams.Length > 0 && !String.IsNullOrEmpty(assemblyParams[0]), "Invalid assembly name arguments passed to domain_AssemblyResolve");
 
            //  Note that there are additional fields passed that indicate the 
            //  version, public key token, etc.  For this demonstration, we           
            //  are just looking at the assembly name.
 
            String assemblyName = assemblyParams[0];
            Assembly loadedAssembly = null;
 
            switch (assemblyName)
            {
                case "Microsoft.TeamFoundation.WorkItemTracking.Controls":
                case "Microsoft.VisualStudio.TeamFoundation":
                    // etc.
                    loadedAssembly = LoadVSPrivateAssembly(assemblyName);
                    break;
 
                default:
                    Debug.Fail(assemblyName + " is not supported by TeamFoundationAssemblyLoadTest.domain_AssemblyResolve");
                    break;
            }
 
            return loadedAssembly;
        }
 
        //  This function will load the named assembly from the Visual Studio PrivateAssemblies 
        //  directory.  This is where a number of Team Foundation assemblies are located that are
        //  not easily accessible to an app.  Fortunately, the .NET loader gives us a shot at 
        //  finding them and we just happen to know where to look.
        static Assembly LoadVSPrivateAssembly(String assemblyName)
        {
            Assembly loadedAssembly = null;
 
            using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\8.0"))
            {
                if (key != null)
                {
                    Object obj = key.GetValue("InstallDir");
 
                    if ((obj != null) && (obj is String))
                    {
                        String vsInstallDir = obj as String;
                        String privateAssembliesDir = Path.Combine(vsInstallDir, "PrivateAssemblies");
                        String assemblyFile = Path.Combine(privateAssembliesDir, assemblyName + ".dll");
 
                        loadedAssembly = Assembly.LoadFile(assemblyFile);
                    }
                    else
                    {
                        Debug.Fail("VS 8.0 InstallDir value is missing or invalid");
                    }
                }
                else
                {
                    Debug.Fail("Could not open VS 8.0 registry key");
                }
            }
 
            return loadedAssembly;
        }
    }
}
 

Comments

  • Anonymous
    March 25, 2006
    I assume this requires admin privledges?  Or, does it get around the fact that an application only has file access to the directory it was loaded from and any sub-directories?
  • Anonymous
    March 25, 2006
    It does not require admin privileges since all that we are doing is reading, not writing.  All this code is doing is getting the assemblies loaded into the process.  The only privileges you require is read access to the registry and file system locations.  Presumably you would have those priveleges to run Visual Studio to begin with.
  • Anonymous
    November 02, 2006
    A question was raised in forums on how to deploy custom controls that refer to team foundation assemblies.