Benutzer mit den meisten Antworten
C#, Winform Applikation, Referenz DLLs und DLLs die als 'PlugIn' drin stehen

Frage
-
Hallo,
ich habe folgende Herausforderung. Der ähnliche Thread ist beantwortet. Danke.
Applikation
- die DLL's beinhaltet bekomme ich mit GetReferencedAssemblies() heraus.
- Jetzt sind noch DLL's als PlugIn drin.
Was würde sich hier anbieten?
- Ich möchte nur meine eigenen bzw. NICHT Windows Assemblies gelistet haben.
Wie könnte das Konzept aussehen?
Ich definiere
a) Das Verzeichnis b) Gehe die Liste der Assemblies durch
Problem --> Es könnten dennoch Leichen drin sein.
Hauptziel
Auf Knopfdruck
Version App.EXE Alle nötigen DLL's mit Versionen.
Viele Grüße Oliver
private void ListReferencedAssemblies() { var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); foreach (var assembly in assemblies) { lstBoxAssemblies.Items.Add(assembly.Name); }
Antworten
-
Hi Oliver,
zum darstellen von Abhängigkeiten gibt es verschiedene Tools.
hier gibt es noch hinweise wie man es selber machen kann.
Ab VS2010 Ultimate bietet da auch einige Möglichkeiten.
Wie euer Factory Pattern aufgebaut ist (wann und wie welche dlls geladen werden), weiß ich jetzt nicht.
Das kann natürlich für Probleme sorgen, aber ich denke da gibt es auch keine trivial Lösung.
- Als Antwort markiert Oliver Stippe Mittwoch, 7. August 2013 18:48
-
Hallo Oliver,
die referenzierten Assemblies zu finden, ist eine leichte Übung. Sobald man die Liste der referenzierten Assemblies hat, kann man die Liste mit dem Ergebnis von Directory.GetFiles() vergleichen. Alles was nicht referenziert ist, ist entweder eine Leiche oder wird dynamisch geladen.
So findest Du die referenzierten Assemblies und gibst sie in ein DataGridView aus (mit sämtlichen versionsrelevanten Infos):
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace ReferencedAssemblies { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // Hier: der Pfad zur aktuellen ausführbaren Datei // aber man kann natürlich auf eine andere Assembly verweisen string mainExe = Application.ExecutablePath; List<AssemblyInfo> referencedAssemblies = GetReferencedAssemblies(mainExe); BindingSource bindingSource = new BindingSource { DataSource = referencedAssemblies }; dataGridView1.DataSource = bindingSource; } private static List<AssemblyInfo> GetReferencedAssemblies(string assemblyPath) { Assembly mainExeAssembly = Assembly.LoadFrom(assemblyPath); List<AssemblyInfo> referencedAssemblies = new List<AssemblyInfo>(); foreach (AssemblyName assemblyName in mainExeAssembly.GetReferencedAssemblies()) { var assemblyInfo = new AssemblyInfo { Name = assemblyName.Name, FullName = assemblyName.FullName, AssemblyVersion = assemblyName.Version.ToString(), CultureInfo = assemblyName.CultureInfo.DisplayName, }; byte[] publicKeyToken = assemblyName.GetPublicKeyToken(); assemblyInfo.IsStrongNamed = publicKeyToken.Length > 0; assemblyInfo.PublicKeyToken = BitConverter.ToString(publicKeyToken).Replace("-", "").ToLower(); assemblyInfo.ProcessorArchitecture = assemblyName.ProcessorArchitecture.ToString(); Assembly resolvedAssembly = Assembly.ReflectionOnlyLoad(assemblyName.FullName); assemblyInfo.IsGlobalAssemblyCache = resolvedAssembly.GlobalAssemblyCache; assemblyInfo.ResolvedAssemblyPath = resolvedAssembly.Location; var fileVersionInfo = FileVersionInfo.GetVersionInfo(resolvedAssembly.Location); assemblyInfo.FileVersion = fileVersionInfo.FileVersion; assemblyInfo.CompanyName = fileVersionInfo.CompanyName; FileInfo fileInfo = new FileInfo(resolvedAssembly.Location); assemblyInfo.FileCreationTimeUtc = fileInfo.CreationTimeUtc; assemblyInfo.FileLastWriteTimeUtc = fileInfo.LastWriteTimeUtc; assemblyInfo.FileSize = fileInfo.Length; byte[] fileHash = new System.Security.Cryptography.SHA1CryptoServiceProvider().ComputeHash(File.OpenRead(resolvedAssembly.Location)); assemblyInfo.FileHash = BitConverter.ToString(fileHash).Replace("-", "").ToLower(); if(!referencedAssemblies.Any(a => a.FullName == assemblyInfo.FullName)) referencedAssemblies.Add(assemblyInfo); } return referencedAssemblies; } } public class AssemblyInfo { public string Name { get; set; } public string FullName { get; set; } public string ResolvedAssemblyPath { get; set; } public string AssemblyVersion { get; set; } public string FileVersion { get; set; } public long FileSize { get; set; } public string FileHash { get; set; } public DateTime FileCreationTimeUtc { get; set; } public DateTime FileLastWriteTimeUtc { get; set; } public string CompanyName { get; set; } public string CultureInfo { get; set; } public string PublicKeyToken { get; set; } public string ProcessorArchitecture { get; set; } public bool IsStrongNamed { get; set; } public bool IsGlobalAssemblyCache { get; set; } public string SatelliteAssemblyPath { get; set; } } }
Bleibt noch die Frage der dynamisch geladenen Assemblies.
Wenn die Assemblies erfolgreich geladen wurden, dann sind sie im von AppDomain.CurrentDomain.GetAssemblies() zurückgegebenen Array zu finden.
Musst Du die Assemblies vor dem Laden identifizieren, dann musst Du die Dokumentation des verwendeten DI-Containers heranziehen. Wie ein DI-Container die verwendeten Schnittstellen auflöst, ist nämlich sehr spezifisch. Manches wird per Konfiguration geladen (aus externen XML-Dateien, aus der app.config, aus der Registrierung, aus Datenbanken etc.), Mappings können aber auch direkt im Code erfolgen.
Noch eine Anmerkung zum Schluss: Die oben verwendete ReflectionOnlyLoad()-Methode erlaubt nur das Ermitteln der Assemblies, die von der geladenen Assembly direkt referenziert werden. Möchtest Du auch deren jeweilige Abhängigkeiten überprüfen, dann musst Du GetReferencedAssemblies() rekursiv aufrufen (nicht ganz trivial).
Als Startpunkt sollte das hoffentlich ausreichend sein.
P.S. Für die Diagnose von Bindungsproblemen würde ich auf fuslogvw.exe zurückgreifen.
Gruß
Marcel
- Bearbeitet Marcel RomaModerator Dienstag, 6. August 2013 22:10
- Als Antwort markiert Oliver Stippe Mittwoch, 7. August 2013 18:47
Alle Antworten
-
Hallo Oliver,
die Knappheit im Ausdruck macht es auch diesmal für mich schwierig zu verstehen, was Du vorhast.
Du schreibst: "Jetzt sind noch DLL's als PlugIn drin". Woher sollen wir denn wissen, was für Plug-ins genau das sind? - Wurden die Plug-ins über das Managed Extensibility Framework (MEF) implementiert, oder über das Managed Add-in Framework (MAF), oder vielleicht sogar "händisch", über eine von deiner Anwendung vorgegebene Schnittstelle? - Das alles wirkt sich auf die Qualität und Methode der Plug-in-Ermittlung.
Wenn alle Plug-ins von dir stammen, könntest Du sie mit einem starken Namen versehen und dabei dieselbe Signaturdatei verwenden. So könntest Du beim Laden der Assembly überprüfen, ob die Assemblies auch wirklich von dir stammen, indem Du den öffentlichen Schlüssel vergleichst (oder nur AssemblyName.GetPublicKeyToken).
Die Version des Plugins muss übrigens nicht immer mit der Version der Datei oder der Assembly übereinstimmen. Je nach verwendeter Plug-in-Architektur gibt es sehr unterschiedliche Vorgehensweisen bei der Versionierung.
Wenn dich nicht die Version des Plug-ins, sondern der Datei interessiert, kannst Du das AssemblyFileVersionAttribute abfragen. Interessiert dich hingegen die Version der Assembly kannst Du AssemblyVersionAttribute abfragen.
Ob darunter auch "Leichen" sind, wirst Du nur feststellen können, wenn Du zuerst definierst, was "lebende" Plug-ins sind und woran man das erkennt.
Gruß
Marcel -
Wenn alle Plug-ins von dir stammen, könntest Du sie mit einem starken Namen versehen und dabei dieselbe Signaturdatei verwenden. So könntest Du beim Laden der Assembly überprüfen, ob die Assemblies auch wirklich von dir stammen, indem Du den öffentlichen Schlüssel vergleichst (oder nur AssemblyName.GetPublicKeyToken). l
Hallo Zusammen,
also kein MEF oder MAF, VS2008, .NET3.5
Das Ziel sollte einfach sein.
Habt Ihr einen Vorschlag, ein konkretes Beispiel. - starken Namen
ListBox, evtl. mit Icons.
- Icon A - richtig referenziert
- Icon B - im Verzeichnis -- Debug oder Release
Assembly A 1.0.0.3, 22.04.2013, Name, Kommentar
Assembly B 1.0.0.3, 19.05.2013, Name, Kommentar
Assembly C 1.5.7.3, 22.07.2013, Name, Kommentar
........
Assembly N 3.0.4.3, 22.04.2013, Name, Kommentar
Sonst dachte ich an so etwas. Je nach Name, ein anderes Icon.
private void ListReferencedAssemblies() { String _Path = Application.StartupPath; // +"\\..\\"; //"..."; string[] Files = System.IO.Directory.GetFiles(_Path, "*.dll", SearchOption.TopDirectoryOnly); String result = String.Empty; foreach (string CurrentFile in Files) { Assembly assembly = Assembly.LoadFrom(CurrentFile); result += assembly.FullName + "\r\n"; } MessageBox.Show(result); //var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); //foreach (var assembly in assemblies) //{ // lstBoxAssemblies.Items.Add(assembly.Name); //}
Viele Grüße Oliver
-
Hallo Oliver,
die fehlenden Antworten sind auch auf deine lakonische Ausdrucksweise zurückzuführen.
Du schreibst: "Das Ziel sollte einfach sein". Die Vorgabe ist sogar knapper als die berühmte spartanische Botschaft "Boote verloren. Mindaros tot. Männer haben Hunger. Wissen nicht, was tun." Den letzten Teil der Botschaft würde ich jedenfalls angesicht deiner verschwommenen Anforderungen sofort unterschreiben.
Kannst Du's bitte noch einmal versuchen? Und diesmal mit Punkt und Komma und in der richtigen Reihenfolge und mit mehr als fünf Wörtern im Satz?
Hier eine kleine Aufbauhilfe in zwei Punkten:
1. Welches Problem versuchst Du zu lösen? - Versuch die Antwort darauf mittels EVA-Prinzip zu strukturieren (Eingabe, Verarbeitung, Ausgabe)
2. Was hast Du bisher versucht, und warum hat das nicht geklappt? - Gab's Fehlermeldungen (exakten Wortlaut mit StackTrace bitte beifügen)?Gruß
Marcel -
1. Welches Problem versuchst Du zu lösen? - Versuch die Antwort darauf mittels EVA-Prinzip zu strukturieren (Eingabe, Verarbeitung, Ausgabe)
2. Was hast Du bisher versucht, und warum hat das nicht geklappt? - Gab's Fehlermeldungen (exakten Wortlaut mit StackTrace bitte beifügen)?Hallo Marcel,
sei nicht böse.
Prinzipiell ist es einfach.Ich habe ein Verzeichnis
bin\ReleaseDas Ziel:
In diesem liegen viele DLL's Komponenten, die meisten sind referenziert,
manche liegen dort, die dann zur Laufzeit über ein Factory Pattern Modellgeladen werden.
Nun gibt es teils Änderungen in den Komponenten und keiner weiß mehr so recht, welche Versionen etc. gelten.
Aus diesem Hintergrund entstand die Idee, gibt es Möglichkeiten zu ermitteln,
wenn die EXE gestartet wird, welche DLL's benötigt werden.-> Listbox -> in eine Textdatei schreiben
-> bei Unstimmigkeiten könnte man nachschauen, welche Varianten liefen.Also DLL's
A) Welche, namentlich mit Version
B) Welche, die referenziert sind.
C) Welche, die zur Laufzeit ohne MEF geladen, benötigt werden.Was habe ich versucht?
Ich nehme jetzt das Verzeichnis und lese die DLL's im Verzeichnis aus.
Mehr nicht. Das Ausgangsziel ist nicht erreicht, ich kann es auch nicht anders formulieren.Es können im Verzeichnis auch Leichen sein.
Deine Idee war mal was mit StrongName, ist mir halt auch neu, Fremdanbieter kann ich ja eh nichts vorgeben.
Ich kann auch von heute auf morgen nicht alles anders machen.
Suche einfache Ansätze.
Irgendwie wird eine korrekte Versionierung gehen, wenn man viel Zeit hätte für die Erarbeitung des Themas.Falls Du noch einen Tipp hast, keine Frager sehr gerne sonst setze ich das Thema als beantwortet.
Grüße Oliver
-
Hi Oliver,
zum darstellen von Abhängigkeiten gibt es verschiedene Tools.
hier gibt es noch hinweise wie man es selber machen kann.
Ab VS2010 Ultimate bietet da auch einige Möglichkeiten.
Wie euer Factory Pattern aufgebaut ist (wann und wie welche dlls geladen werden), weiß ich jetzt nicht.
Das kann natürlich für Probleme sorgen, aber ich denke da gibt es auch keine trivial Lösung.
- Als Antwort markiert Oliver Stippe Mittwoch, 7. August 2013 18:48
-
Hallo Oliver,
die referenzierten Assemblies zu finden, ist eine leichte Übung. Sobald man die Liste der referenzierten Assemblies hat, kann man die Liste mit dem Ergebnis von Directory.GetFiles() vergleichen. Alles was nicht referenziert ist, ist entweder eine Leiche oder wird dynamisch geladen.
So findest Du die referenzierten Assemblies und gibst sie in ein DataGridView aus (mit sämtlichen versionsrelevanten Infos):
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Windows.Forms; namespace ReferencedAssemblies { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { // Hier: der Pfad zur aktuellen ausführbaren Datei // aber man kann natürlich auf eine andere Assembly verweisen string mainExe = Application.ExecutablePath; List<AssemblyInfo> referencedAssemblies = GetReferencedAssemblies(mainExe); BindingSource bindingSource = new BindingSource { DataSource = referencedAssemblies }; dataGridView1.DataSource = bindingSource; } private static List<AssemblyInfo> GetReferencedAssemblies(string assemblyPath) { Assembly mainExeAssembly = Assembly.LoadFrom(assemblyPath); List<AssemblyInfo> referencedAssemblies = new List<AssemblyInfo>(); foreach (AssemblyName assemblyName in mainExeAssembly.GetReferencedAssemblies()) { var assemblyInfo = new AssemblyInfo { Name = assemblyName.Name, FullName = assemblyName.FullName, AssemblyVersion = assemblyName.Version.ToString(), CultureInfo = assemblyName.CultureInfo.DisplayName, }; byte[] publicKeyToken = assemblyName.GetPublicKeyToken(); assemblyInfo.IsStrongNamed = publicKeyToken.Length > 0; assemblyInfo.PublicKeyToken = BitConverter.ToString(publicKeyToken).Replace("-", "").ToLower(); assemblyInfo.ProcessorArchitecture = assemblyName.ProcessorArchitecture.ToString(); Assembly resolvedAssembly = Assembly.ReflectionOnlyLoad(assemblyName.FullName); assemblyInfo.IsGlobalAssemblyCache = resolvedAssembly.GlobalAssemblyCache; assemblyInfo.ResolvedAssemblyPath = resolvedAssembly.Location; var fileVersionInfo = FileVersionInfo.GetVersionInfo(resolvedAssembly.Location); assemblyInfo.FileVersion = fileVersionInfo.FileVersion; assemblyInfo.CompanyName = fileVersionInfo.CompanyName; FileInfo fileInfo = new FileInfo(resolvedAssembly.Location); assemblyInfo.FileCreationTimeUtc = fileInfo.CreationTimeUtc; assemblyInfo.FileLastWriteTimeUtc = fileInfo.LastWriteTimeUtc; assemblyInfo.FileSize = fileInfo.Length; byte[] fileHash = new System.Security.Cryptography.SHA1CryptoServiceProvider().ComputeHash(File.OpenRead(resolvedAssembly.Location)); assemblyInfo.FileHash = BitConverter.ToString(fileHash).Replace("-", "").ToLower(); if(!referencedAssemblies.Any(a => a.FullName == assemblyInfo.FullName)) referencedAssemblies.Add(assemblyInfo); } return referencedAssemblies; } } public class AssemblyInfo { public string Name { get; set; } public string FullName { get; set; } public string ResolvedAssemblyPath { get; set; } public string AssemblyVersion { get; set; } public string FileVersion { get; set; } public long FileSize { get; set; } public string FileHash { get; set; } public DateTime FileCreationTimeUtc { get; set; } public DateTime FileLastWriteTimeUtc { get; set; } public string CompanyName { get; set; } public string CultureInfo { get; set; } public string PublicKeyToken { get; set; } public string ProcessorArchitecture { get; set; } public bool IsStrongNamed { get; set; } public bool IsGlobalAssemblyCache { get; set; } public string SatelliteAssemblyPath { get; set; } } }
Bleibt noch die Frage der dynamisch geladenen Assemblies.
Wenn die Assemblies erfolgreich geladen wurden, dann sind sie im von AppDomain.CurrentDomain.GetAssemblies() zurückgegebenen Array zu finden.
Musst Du die Assemblies vor dem Laden identifizieren, dann musst Du die Dokumentation des verwendeten DI-Containers heranziehen. Wie ein DI-Container die verwendeten Schnittstellen auflöst, ist nämlich sehr spezifisch. Manches wird per Konfiguration geladen (aus externen XML-Dateien, aus der app.config, aus der Registrierung, aus Datenbanken etc.), Mappings können aber auch direkt im Code erfolgen.
Noch eine Anmerkung zum Schluss: Die oben verwendete ReflectionOnlyLoad()-Methode erlaubt nur das Ermitteln der Assemblies, die von der geladenen Assembly direkt referenziert werden. Möchtest Du auch deren jeweilige Abhängigkeiten überprüfen, dann musst Du GetReferencedAssemblies() rekursiv aufrufen (nicht ganz trivial).
Als Startpunkt sollte das hoffentlich ausreichend sein.
P.S. Für die Diagnose von Bindungsproblemen würde ich auf fuslogvw.exe zurückgreifen.
Gruß
Marcel
- Bearbeitet Marcel RomaModerator Dienstag, 6. August 2013 22:10
- Als Antwort markiert Oliver Stippe Mittwoch, 7. August 2013 18:47
-
Als Startpunkt sollte das hoffentlich ausreichend sein.
P.S. Für die Diagnose von Bindungsproblemen würde ich auf fuslogvw.exe zurückgreifen.
Hallo Marcel,
ja und Danke nochmals!
Grüße Oliver