Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using an absolute path in probing privatePath

Tags:

c#

In a C# console application, I'm trying to use <probing privatePath=""/> to point to dlls that are not in my application subdirectories. I'm using:

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="D:\Library\References" />
    </assemblyBinding>
</runtime>    

This does not work, because privatePath is looking for subdirectories in my application. Is there a way to use absolute paths in this way? If not, what is the best way to point to dlls that are located outside of my application? I also tried using <codebase> with a file:/// path, but still got a System.IO.FileNotFound exception.

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity 
        name="MyLibrary" publicKeyToken="29989D7A39ACF230" />
      <codeBase
        version="2.0.0.0"
        href="http://file:///D:/Library/References/NLog.dll"/>
    </dependentAssembly>
  </assemblyBinding>
</runtime>

but still got a System.IO.FileNotFound exception.

Thanks!

like image 889
Paul Avatar asked Mar 02 '12 15:03

Paul


2 Answers

Use an AssemblyResolver instead in this situation.

Here's some code I cribbed partly from another question and modified for our own use. Unlike the linked code, this one resolves the application execution folder, which was something I haven't seen in many other examples. Feel free to excise that and stick in your own absolute path if necessary.

One advantage of the assembly resolver is that if you have mixed versions of your dlls, and want to load the dll from the target folder, not the one that happens to be with the app, then this works whereas the config file method doesn't.

I have that problem because our app ships with a small utility that is an app upgrader, and it often needs to reference newer versions of the dlls than the original app. (The upgrader gets updated, then the upgrader updates the main app. If they both look at the same dlls, bad things can happen.)

public static class AssemblyResolver
{
    internal static void Hook(params string[] folders)
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            // Check if the requested assembly is part of the loaded assemblies
            var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name);
            if (loadedAssembly != null)
                return loadedAssembly;

            // This resolver is called when a loaded control tries to load a generated XmlSerializer - We need to discard it.
            // http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization

            var n = new AssemblyName(args.Name);

            if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase))
                return null;

            // http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl

            if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
                return null;

            string assy = null;
            // Get execution folder to use as base folder
            var rootFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)??"";

            // Find the corresponding assembly file
            foreach (var dir in folders)
            {
                assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(Path.Combine(rootFolder,dir), g)).FirstOrDefault(f =>
                {
                    try
                    {
                        return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name,
                            StringComparison.OrdinalIgnoreCase);
                    }
                    catch (BadImageFormatException)
                    {
                        return false; /* Bypass assembly is not a .net exe */
                    }
                    catch (Exception ex)
                    {
                        // Logging etc here
                        throw;
                    }
                });

                if (assy != null)
                    return Assembly.LoadFrom(assy);
            }

            // More logging for failure here
            return null;
        };
    }
}

Invoke this early on providing a list of paths to use for assembly resolving

AssemblyResolver.Hook("upglib","myOtherFolder");
like image 120
dylanT Avatar answered Oct 29 '22 06:10

dylanT


according to MSDN:

You can use the element only in machine configuration or publisher policy files that also redirect the assembly version. ... If you are supplying a code base hint for an assembly that is not strong-named, the hint must point to the application base or a subdirectory of the application base directory.

You probably tried to apply in in app.config?

and

The directories specified in privatePath must be subdirectories of the application base directory.

like image 17
the_joric Avatar answered Oct 29 '22 06:10

the_joric