Meine DLLs werden von einer Anwendung von Drittanbietern geladen, die wir nicht anpassen können. Meine Assemblies müssen sich in einem eigenen Ordner befinden. Ich kann sie nicht in GAC einfügen (meine Anwendung muss mit XCOPY bereitgestellt werden). Wenn die Root-DLL versucht, eine Ressource oder einen Typ von einer anderen DLL (im selben Ordner) zu laden, schlägt das Laden fehl (FileNotFound). Ist es möglich, den Ordner, in dem sich meine DLLs befinden, dem Assembly-Suchpfad programmgesteuert hinzuzufügen (aus der Stamm-DLL)? Ich darf die Konfigurationsdateien der Anwendung nicht ändern.Wie fügt man zur Laufzeit in .NET den Ordner zum Assembly-Suchpfad hinzu?
Antwort
Sie können der Datei CONFIG Ihrer Anwendung eine probing path hinzufügen. Dies funktioniert jedoch nur, wenn der Prüfpfad im Basisverzeichnis Ihrer Anwendung enthalten ist.
Klingt, als könnten Sie das AppDomain.AssemblyResolve-Ereignis verwenden und die Abhängigkeiten manuell von Ihrem DLL-Verzeichnis laden.
Edit (aus dem Kommentar):
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;
}
Danke, Mattias! Dies funktioniert: AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve + = neuer ResolveEventHandler (LoadFromSameFolderResolveEventHandler); statische Assembly LoadFromSameFolderResolveEventHandler (Objekt Absender, ResolveEventArgs Args) { Zeichenfolge Ordnerpfad = Path.GetDirectoryName (Assembly.GetExecutingAssembly(). Ort); Zeichenfolge assemblyPath = Path.Combine (folderPath, args.Name + ".dll"); Baugruppe assembly = Assembly.LoadFrom (assemblyPath); Rückholeinheit; } – isobretatel
Große Lösung! Hier ist ein Artikel beschreibt einige andere Lösungen einschließlich dieser im Detail: http://kbalertz.com/897297/consume-assemblies-located-folder-different-application-folder-Visual-Basic.aspx (Warnung: es ist für VB , nicht C# und ein wenig alt) – Domenic
Was würden Sie tun, wenn Sie auf den grundlegenden Resolver "Fallback" wollten. z.B. 'if (! File.Exists (asmPath)) return searchInGAC (...);' –
Blick in AppDomain.AppendPrivatePath (veraltet) oder AppDomainSetup.PrivateBinPath
Von [MSDN] (http://msdn.microsoft.com/en-us/library/system.appdomainsetup.aspx): Ändern Die Eigenschaften einer AppDomainSetup-Instanz wirken sich nicht auf vorhandene AppDomains aus. Es kann nur die Erstellung einer neuen Anwendungsdomäne beeinflussen, wenn die CreateDomain-Methode mit der AppDomainSetup-Instanz als Parameter aufgerufen wird. – Nathan
['AppDomain.AppendPrivatePath'] (https://msdn.microsoft.com/en-us/library/system.appdomain.appendprivatepath%28v=vs.110%29.aspx) scheint die Dokumentation darauf hindeuten, dass es sollte unterstützt die dynamische Erweiterung des Suchpfads 'AppDomain', nur dass das Feature veraltet ist. Wenn es funktioniert, ist es eine viel sauberere Lösung als "AssemblyResolve" zu überladen. – binki
Die beste Erklärung from MS itself:
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;
}
'AssemblyResolve' ist für CurrentDomain, nicht gültig für eine andere Domäne' AppDomain.CreateDomain' – Kiquenet
Update für Framework 4
Seit Framework 4 die Assembl erhöhen yResolve-Ereignis auch für Ressourcen tatsächlich dieser Handler besser funktioniert. Es basiert auf dem Konzept, dass sich Lokalisationen in App-Unterverzeichnissen befinden (eines für die Lokalisierung mit dem Namen der Kultur, d. H. C: \ MyApp \ it für Italienisch). Darin befinden sich Ressourcen-Dateien. Der Handler funktioniert auch, wenn die Lokalisierung eine Länderregion ist, d.h. it-IT oder pt-BR. In diesem Fall könnte der Handler "mehrmals aufgerufen werden: einmal für jede Kultur in der Fallback-Kette" [von MSDN]. Dies bedeutet, dass, wenn wir null für die Ressourcendatei "it-IT" zurückgeben, das Framework das Ereignis auslöst und nach "it" fragt.
Ereignis Haken
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(currentDomain_AssemblyResolve);
Eventhandler
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;
}
}
Für C++/CLI-Benutzer, hier ist die Antwort ‚@Mattias S (was für mich funktioniert):
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);
I habe @ Mattias S 'Lösung verwendet. Wenn Sie Abhängigkeiten aus dem gleichen Ordner tatsächlich auflösen möchten, sollten Sie versuchen, Assembly Speicherort zu verwenden, wie unten gezeigt. args.RequestAssembly sollte auf Nichtigkeit überprüft werden.
- 1. Wie fügt man Eigenschaften zum Objekt zur Laufzeit in .net hinzu?
- 2. Wie fügt man Einträge in Application.conf zur Laufzeit hinzu?
- 3. C# Wie fügt man contextMenuStrip zur Laufzeit hinzu?
- 4. Wie fügt man InputMethodService zur Aktivität hinzu?
- 5. Wie fügt man Zeichenketten zur Textdatei hinzu?
- 6. Wie fügt man Imageview zum Fragment hinzu?
- 7. Ember-Router: Wie fügt man eine Route zur Laufzeit in Ember 1.0-rc2 hinzu?
- 8. Wie fügt man Ansichten zur Scroll-Ansicht in Android hinzu?
- 9. Wie fügt man zur Laufzeit ein Benutzersteuerelement zu einem Fenster hinzu?
- 10. Wie fügt man Widget zur header.php in Wordpress hinzu?
- 11. Wie fügt man CoreData.framework zum vorhandenen xcode 4.6.2 Projekt hinzu
- 12. Wie fügt man Java-JAR-Dateien zum Grails-Projekt hinzu?
- 13. Wie fügt man dem Grails-Domänenobjekt dynamische Methoden zur Laufzeit hinzu, ohne Plugins zu erstellen?
- 14. Wie fügt man der tabellierten Registerkartensteuerung zur Laufzeit ein wpf-Steuerelement hinzu?
- 15. Wie fügt man Brotkrume hinzu?
- 16. Wie fügt man dem ClassPath zur Laufzeit dynamisch eine externe jar-Datei hinzu?
- 17. C#: Wie fügt man einem Objekt zur Laufzeit ein Attribut hinzu?
- 18. Wie fügt man "vcl.bpi" zum BPL-Projekt hinzu
- 19. Wie fügt man mehrere Filter zur Abfrage hinzu?
- 20. Wie fügt man Delphi-Code zur Versionskontrolle hinzu?
- 21. Wie fügt man angularMoment zur Rails-Manifestdatei hinzu?
- 22. Wie fügt man accessateType zur Unteransicht von tableviewcell hinzu
- 23. Wie fügt man die Option zur Deinstallation in .NET Setup Project hinzu?
- 24. Wie fügt man benutzerdefiniertes Element zum Systemmenü in C++ hinzu?
- 25. Wie fügt man Widget zum Layout in Magento 2 hinzu?
- 26. Wie fügt man Tags zum Abschluss des Organisationsmodus hinzu?
- 27. Wie fügt man Video zur HTML-Seite hinzu?
- 28. Wie fügt man die Bildlaufleiste zur Webseite hinzu?
- 29. Wie fügt man Namespace zur Razor View Engine hinzu?
- 30. Mandril: Wie fügt man eine ics-Einladungsdatei zur Vorlage hinzu?
Danke für das Hinzufügen. Ich habe die 'AssemblyResolve' Lösung so oft gesehen, gut um eine andere (und einfachere) Option zu haben. –