¿Cómo agregar la carpeta a la ruta de búsqueda del ensamblado en el tiempo de ejecución en .NET?

Mis archivos DLL son cargados por una aplicación de terceros, que no podemos personalizar. Mis ensamblajes deben estar ubicados en su propia carpeta. No puedo ponerlos en GAC (mi aplicación tiene un requisito para implementarse usando XCOPY). Cuando la DLL raíz intenta cargar el recurso o escribir desde otra DLL (en la misma carpeta), la carga falla (FileNotFound). ¿Es posible agregar la carpeta donde se encuentran mis archivos DLL a la ruta de búsqueda de ensamblados mediante progtwigción (desde la DLL de la raíz)? No tengo permiso para cambiar los archivos de configuración de la aplicación.

Parece que podría usar el evento AppDomain.AssemblyResolve y cargar manualmente las dependencias de su directorio DLL.

Editar (del comentario):

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; } 

Puede agregar una ruta de prueba al archivo .config de su aplicación, pero solo funcionará si la ruta de exploración está contenida en el directorio base de su aplicación.

Actualización para el Marco 4

Dado que Framework 4 plantea el evento AssemblyResolve también para los recursos, en realidad este controlador funciona mejor. Se basa en el concepto de que las localizaciones se encuentran en subdirectorios de aplicaciones (una para la localización con el nombre de la cultura, es decir, C: \ MyApp \ it para italiano). Dentro hay un archivo de recursos. El controlador funciona también si la localización es country-region, es decir it-IT o pt-BR. En este caso, el controlador “podría llamarse varias veces: una vez para cada cultura en la cadena alternativa” [de MSDN]. Esto significa que si devolvemos el valor nulo para el archivo de recursos “IT-IT”, el marco plantea el evento que solicita el “it”.

Gancho del evento

  AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve); 

Controlador de eventos

  Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { //This handler is called only when the common language runtime tries to bind to the assembly and fails. Assembly executingAssembly = Assembly.GetExecutingAssembly(); string applicationDirectory = Path.GetDirectoryName(executingAssembly.Location); string[] fields = args.Name.Split(','); string assemblyName = fields[0]; string assemblyCulture; if (fields.Length < 2) assemblyCulture = null; else assemblyCulture = fields[2].Substring(fields[2].IndexOf('=') + 1); string assemblyFileName = assemblyName + ".dll"; string assemblyPath; if (assemblyName.EndsWith(".resources")) { // Specific resources are located in app subdirectories string resourceDirectory = Path.Combine(applicationDirectory, assemblyCulture); assemblyPath = Path.Combine(resourceDirectory, assemblyFileName); } else { assemblyPath = Path.Combine(applicationDirectory, assemblyFileName); } if (File.Exists(assemblyPath)) { //Load the assembly from the specified path. Assembly loadingAssembly = Assembly.LoadFrom(assemblyPath); //Return the loaded assembly. return loadingAssembly; } else { return null; } } 

La mejor explicación de MS en sí :

 AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler); private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) { //This handler is called only when the common language runtime tries to bind to the assembly and fails. //Retrieve the list of referenced assemblies in an array of AssemblyName. Assembly MyAssembly, objExecutingAssembly; string strTempAssmbPath = ""; objExecutingAssembly = Assembly.GetExecutingAssembly(); AssemblyName[] arrReferencedAssmbNames = objExecutingAssembly.GetReferencedAssemblies(); //Loop through the array of referenced assembly names. foreach(AssemblyName strAssmbName in arrReferencedAssmbNames) { //Check for the assembly names that have raised the "AssemblyResolve" event. if(strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(","))) { //Build the path of the assembly from where it has to be loaded. strTempAssmbPath = "C:\\Myassemblies\\" + args.Name.Substring(0,args.Name.IndexOf(","))+".dll"; break; } } //Load the assembly from the specified path. MyAssembly = Assembly.LoadFrom(strTempAssmbPath); //Return the loaded assembly. return MyAssembly; } 

Para los usuarios de C ++ / CLI, aquí está la respuesta @Mattias S ‘(que funciona para mí):

 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); 

ver en AppDomain.AppendPrivatePath (en desuso) o AppDomainSetup.PrivateBinPath

He usado la solución @Mattias S ‘. Si realmente desea resolver las dependencias de la misma carpeta, intente utilizar Requesting assembly location, como se muestra a continuación. args.RequestingAssembly debe verificarse para la nulidad.

 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; };