How to add folder to assembly search path at runtime in .NET?

前端 未结 8 2080
慢半拍i
慢半拍i 2020-11-22 07:08

My DLLs are loaded by a third-party application, which we can not customize. My assemblies have to be located in their own folder. I can not put them into GAC (my applicatio

相关标签:
8条回答
  • 2020-11-22 07:43

    look into AppDomain.AppendPrivatePath (deprecated) or AppDomainSetup.PrivateBinPath

    0 讨论(0)
  • 2020-11-22 07:44

    Sounds like you could use the AppDomain.AssemblyResolve event and manually load the dependencies from your DLL directory.

    Edit (from the comment):

    AppDomain currentDomain = AppDomain.CurrentDomain;
    currentDomain.AssemblyResolve += new ResolveEventHandler(LoadFromSameFolder);
    
    static Assembly LoadFromSameFolder(object sender, ResolveEventArgs args)
    {
        string folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        string assemblyPath = Path.Combine(folderPath, new AssemblyName(args.Name).Name + ".dll");
        if (!File.Exists(assemblyPath)) return null;
        Assembly assembly = Assembly.LoadFrom(assemblyPath);
        return assembly;
    }
    
    0 讨论(0)
  • 2020-11-22 07:46

    I've used @Mattias S' solution. If you actually want to resolve dependencies from the same folder - you should try using Requesting assembly location, as shown below. args.RequestingAssembly should be checked for nullity.

    System.AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
    {
        var loadedAssembly = System.AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName == args.Name).FirstOrDefault();
        if(loadedAssembly != null)
        {
            return loadedAssembly;
        }
    
        if (args.RequestingAssembly == null) return null;
    
        string folderPath = Path.GetDirectoryName(args.RequestingAssembly.Location);
        string rawAssemblyPath = Path.Combine(folderPath, new System.Reflection.AssemblyName(args.Name).Name);
    
        string assemblyPath = rawAssemblyPath + ".dll";
    
        if (!File.Exists(assemblyPath))
        {
            assemblyPath = rawAssemblyPath + ".exe";
            if (!File.Exists(assemblyPath)) return null;
        } 
    
        var assembly = System.Reflection.Assembly.LoadFrom(assemblyPath);
        return assembly;
     };
    
    0 讨论(0)
  • 2020-11-22 07:47

    For C++/CLI users, here is @Mattias S' answer (which works for me):

    using namespace System;
    using namespace System::IO;
    using namespace System::Reflection;
    
    static Assembly ^LoadFromSameFolder(Object ^sender, ResolveEventArgs ^args)
    {
        String ^folderPath = Path::GetDirectoryName(Assembly::GetExecutingAssembly()->Location);
        String ^assemblyPath = Path::Combine(folderPath, (gcnew AssemblyName(args->Name))->Name + ".dll");
        if (File::Exists(assemblyPath) == false) return nullptr;
        Assembly ^assembly = Assembly::LoadFrom(assemblyPath);
        return assembly;
    }
    
    // put this somewhere you know it will run (early, when the DLL gets loaded)
    System::AppDomain ^currentDomain = AppDomain::CurrentDomain;
    currentDomain->AssemblyResolve += gcnew ResolveEventHandler(LoadFromSameFolder);
    
    0 讨论(0)
  • 2020-11-22 07:47

    I came here from another (marked duplicate) question about adding the probing tag to the App.Config file.

    I want to add a sidenote to this - Visual studio had already generated an App.config file, however adding the probing tag to the pregenerated runtime tag did not work! you need a seperate runtime tag with the probing tag included. In short, your App.Config should look like this:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <startup> 
            <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
        </startup>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
    
      <!-- Discover assemblies in /lib -->
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <probing privatePath="lib" />
        </assemblyBinding>
      </runtime>
    </configuration>
    

    This took some time to figure out so I am posting it here. Also credits to The PrettyBin NuGet Package. It is a package that moves the dlls automatically. I liked a more manual approach so I did not use it.

    Also - here is a post build script that copies all .dll/.xml/.pdb to /Lib. This unclutters the /debug (or /release) folder, what I think people try to achieve.

    :: Moves files to a subdirectory, to unclutter the application folder
    :: Note that the new subdirectory should be probed so the dlls can be found.
    SET path=$(TargetDir)\lib
    if not exist "%path%" mkdir "%path%"
    del /S /Q "%path%"
    move /Y $(TargetDir)*.dll "%path%"
    move /Y $(TargetDir)*.xml "%path%"
    move /Y $(TargetDir)*.pdb "%path%"
    
    0 讨论(0)
  • 2020-11-22 07:49

    You can add a probing path to your application's .config file, but it will only work if the probing path is a contained within your application's base directory.

    0 讨论(0)
提交回复
热议问题