Resolve assembly references from another folder

27,439

You should first find the folder where theses dlls are installed then use AppDomain.AssemblyResolve to hook assembly resolution and try to load the requested assemblies from this folder.

It will look something like this (not tested, and you need to check what args.Name contain exactly, could contain the version and the strong name along with type name) :

var otherCompanyDlls = new DirectoryInfo(companyFolder).GetFiles("*.dll");

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name);
    if (dll == null)
    {
        return null;
    }

    return Assembly.Load(dll.FullName);
};
Share:
27,439
Davide Piras
Author by

Davide Piras

...

Updated on March 11, 2020

Comments

  • Davide Piras
    Davide Piras about 4 years

    I am developing an application which references and uses some third party assemblies from a certain Vendor; in development box I have these 3 assemblies in a reference folder in my source tree and I can reference them and build the application, application builds but does not run because the whole server application is not installed, but this is fine.

    On the server where I want to copy this custom application and run all assemblies I am referencing are in folder something like:

    D:\ProgramFiles\VendorName\ProductName\Support\API\Bin64
    

    and if I copy my small executable in that folder and run it, it works perfectly, but if I put my .exe in a more appropriate folder like I want:

    D:\ProgramFiles\MyCompanyName\MyProduct\bin\...
    

    it does not work because it cannot resolve those assemblies.

    I know I can use probing in app.config to specify in which folders my exe has to look for references but imy case the assemblies are not in a subfolder, more in a completely different location.

    I don't want to copy all vendor assemblies in my app folder and I cannot put there only the 3 ones I am referencing because they are also loading other assemblies and unless I have all of them (many...), it does not work.

    I am not doing anything special, not creating app domains and not loading assemblies via reflection, just want the CLR to resolve the references as they are needed on application start or execution.

    Thanks.

    Edit: here the final working code

    static System.Reflection.Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        Logger logger = new Logger();
    
        try
        {
            string RMSAssemblyFolder = ConfigurationManager.AppSettings["RMSAssemblyFolder"];
    
            Assembly MyAssembly = null;
            string strTempAssmbPath = string.Empty;
    
            Assembly objExecutingAssemblies = Assembly.GetExecutingAssembly();
            AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
    
            AssemblyName myAssemblyName = Array.Find<AssemblyName>(arrReferencedAssmbNames, a => a.Name == args.Name);
    
            if (myAssemblyName != null)
            {
                MyAssembly = Assembly.LoadFrom(myAssemblyName.CodeBase);
            }
            else
            {
                strTempAssmbPath = Path.Combine(RMSAssemblyFolder, args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll");
    
                if (!string.IsNullOrEmpty(strTempAssmbPath))
                {
                    if (File.Exists(strTempAssmbPath))
                    {
                        logger.Information("Assembly to load: {0} - File was found in: {1}", args.Name, strTempAssmbPath);
    
                        // Loads the assembly from the specified path.                  
                        MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
                    }
                }
            }
    
            // Returns the loaded assembly.
            return MyAssembly;
        }
        catch (Exception exc)
        {
            logger.Error(exc);
            return null;
        }
    }
    
  • Davide Piras
    Davide Piras about 13 years
    Hi, wish it worked, I still get the FileNotFound exception... the assembly I need to reference is not strongly named I guess, SN -t did not work but I saw the PublicKeyToken from the exception message so I copied its value from there, still cannot be resolved... :(
  • Davide Piras
    Davide Piras about 13 years
    Thanks, I have fixed using your idea and my code in the edited question and now works fine :)
  • Davide Piras
    Davide Piras about 13 years
    Surely my solution can be optimized, I am not sure if this method has to return null if an assembly is not found in the custom folder and if so, the CLR will still look for it in the GAC, because for example I have noticed this method is also called for assemblies like System.XML.Serializer... I am testing this in a console application which hosts a netTcp bound WCF Service, now it works and I can run the app from any folder, in the end I will create a WindowsService to host my WCF. Thanks Again .))
  • Per Lundberg
    Per Lundberg about 11 years
    I've found that this approach doesn't work for me. Assembly.Load() with a full path fails; it calls the AssemblyResolve once again (since it cannot resolve the DLL). So I'm currently looking into using Assembly.LoadFrom() within the AssemblyResolve handler instead.
  • George Mamaladze
    George Mamaladze over 10 years
    'var dll = otherCompanyDlls.FirstOrDefault(fi => fi.Name == args.Name)' - causes the lienear scan of the list on every hit or miss. With a big number oaf requested and existing assemblies may be a performance issue.
  • Julien Roncaglia
    Julien Roncaglia over 10 years
    It's a linear scan over a small set (number of dlls in the folder) each time some assembly doesn't resolve (Max once per unknown assembly per load context). This smell like premature optimization (1. Don’t do it. 2. For experts only!: Don’t do it yet). but anyway a simple var otherCompanyDllsDict = otherCompanyDlls.ToDictionary(fi => fi.Name) and then access by the dictionary should solve it if it happens.
  • Kiquenet
    Kiquenet over 8 years
    AssemblyResolve is for CurrentDomain, not valid for another domain AppDomain.CreateDomain