Laden...

ilmerge Zugriff auf in x.exe gemergte a.dll von b.dll aus

Erstellt von Cornflake vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.845 Views
C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren
ilmerge Zugriff auf in x.exe gemergte a.dll von b.dll aus

Hallo Leute
vllt habt ihr ja einen Tipp. (VS2008, WinForms, .Net 3.5)

Ich hab eine Solution mit ein paar Projekten, die ungefähr wie folgt zusammenhängen.

tool.exe verweist auf: Iplugin.dll, ui.dll, data.dll, logic.dll, helper.dll, zip.dll
Da es ein kleiners Tool ist, dass ich einfach nur als Single-exe per Copy ausliefern will, habe ich mit ilmerge.exe alles zusammengemergt.
Ergebnis eine tool.exe ohne zusätzliche <x>.dll's im Ordner und es funktioniert.

Problem:
Jetzt will ich dazu als Plugin eine Erweiterung (ext.dll) per assembly.Load(byte[])... laden. wichtig ich nutze das per byte Array und nicht per Stringdateipfad Angabe.

ext.dll verweist auf: Iplugin.dll, helper.dll, zip.dll
Hier will ich auch wieder alles mit ilmerge zusammenpacken, damit ich zum Schluss maximal 2 Dateien in den Ordner kopieren brauche.

?( Das eigentliche Problem ist, wie verweise ich richtig aus der ext.dll auf die Iplugin.dll,helper.dll und zip.dll, wenn diese durch den ilmerge nicht im Ordner sondern in der tool.exe liegen.
Im VS kann ich ja auf die Projekte referenzieren, allerdings bringt ilmerge verständlicher weise bei der ext.dll ne Fehlermeldung im Logfile, dass es die anderen dll's nicht finden kann.

Kann ich da etwas in ext.csproj Datei anpassen, um auf die dll's innerhalb der tool.exe zu verweisen, bzw. gibt es in ilmerge.exe eine Konfigurationsmöglichkeit oder gibt es irgendwelche Klassenattribute die da weiterhelfen?

Grüße Cornflake

Anbei angepasster ilmerge Log Ausschnitt:


...
AssemblyResolver: Assembly 'Tool.Plugin.Ext' is referencing assembly 'Helper'.
	AssemblyResolver: Attempting referencing assembly's directory.
	AssemblyResolver: Did not find assembly in referencing assembly's directory.
	AssemblyResolver: Attempting input directory.
	AssemblyResolver: Did not find assembly in input directory.
	AssemblyResolver: Attempting user-supplied directories.
	AssemblyResolver: No user-supplied directories.
	AssemblyResolver: Attempting framework directory.
	AssemblyResolver: Did not find assembly in framework directory.
AssemblyResolver: Unable to resolve reference. (It still might be found, e.g., in the GAC.)
...
An exception occurred during merging:
Unresolved assembly reference not allowed: Helper.
   bei System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
   bei System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
   bei System.Compiler.Ir2md.VisitReferencedType(TypeNode type)
   bei System.Compiler.Ir2md.VisitClass(Class Class)
   bei System.Compiler.Ir2md.VisitModule(Module module)
   bei System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
   bei System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation, BinaryWriter writer)
   bei System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName)
   bei System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Module module)
   bei ILMerging.ILMerge.Merge()
   bei ILMerging.ILMerge.Main(String[] args)


16.834 Beiträge seit 2008
vor 8 Jahren

Du musst dann auf die ext.dll verweisen, statt auf die helper.dll

Einfacher wäre es ohnehin, wenn Du andere DLLs - wenn diese vorher existieren und eingebettet werden sollen - diese einfach als eingebettete Ressource behandelst.
Dann kannst sie via GetManifestResourceStream die Bytes laden und dann einfach Assembly.Load(bytes).

Dann kann man sich auch den ganzen ILMerge Quatsch sparen.

D
152 Beiträge seit 2013
vor 8 Jahren
C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren

Hi
Danke für eure Tipps. 😃
zlib, werde ich später ausprobieren, wenn das hier nicht klappen sollte.

Von ilMerge versuche ich jetzt weg zu kommen.
Habe dazu folgenden Link gelesen: Jeffrey Richter: Excerpt #2 from CLR via C#, Third Edition

Da geht es darum, die referenzierten Dll's als eingebettete Ressourcen innerhalb des Projektes zusätzlich hinzuzufügen und dann mit einem Codeblock zu laden, sobald die CLR einen Fehler meldet.
Leider funzt das bei mir noch nicht ganz.

Zum nachladen der DLL verwendet er den Code:


AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

}; 

Wenn ich nach dem ersten Kompilieren das Programm starte geht alles. Wenn ich dann für den zweiten start die im Ordner liegenden *.dll vorher entferne, die ich als eingebettete Ressource eingebunden haben und per Code laden lassen will, kommt es zu einer CLR Fehlermeldung, dass er die dlls nicht mehr findet. Ich glaube daher der Nachladecode ist noch falsch (Syntax / Position).

Aktuell steht der in meiner tool.exe Main Methode im Projekt ganz am Anfang.
Die Fehlermeldung kommt aber noch vor dem Debug Einsprungpunkt.


/// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {

            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";
                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            }; 

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
...

Habt ihr ne Idee?

D
152 Beiträge seit 2013
vor 8 Jahren

libz bindet die DLLs einfach als Ressource ein.
Einfach kurz ausprobieren

libz inject-dll --assembly MyApplication.exe --include *.dll --move
C
Cornflake Themenstarter:in
142 Beiträge seit 2007
vor 8 Jahren

Ich habs FREU 😁

Es funktionieren damit zwei Themen:

  1. Die tool.exe beinhaltet alle *.dll's und der Ordner ist sauber.
  2. Das Verfahren (ResolveEventHandler) auch auf das Plugin angewendet behebt auch die Fehlermeldung mit den fehlenden Dll's der oben genannten ext.dll.

Sobald in der Main() irgendwo eine Instanz der eingebetteten Dll verwendet werden soll, will die CLR dies auflösen und es kommt zu einem Fehler. Daher kann er die Fehlerbehandlung auch noch gar nicht einrichten.

Der Trick ist folgender:
In der Main darf nur die Fehlerbehandlung stehen und danach kann eine zweite Main aufgerufen werden, in der der bisherige Code steht. Dann wird die Fehlerbehandlung eingerichtet und dann beim eigentlichen Main aufruf knallt die CLR, aber wird gleich von der eingerichteten Fehlerbehandlung aufgefangen.

⚠Sollte dennoch ein Fehler später kommen, wurde evtl. vergessen eine eingebundenen dll als eingebettete Ressource zu markieren. Zudem habe ich den Präfixtext beim ermittelten Dll Namen rausgenommen. Zusätzlich ist zu beachten, dass wenn man von Debug auf Release umstellt, evtl. die Dll neu als eingebettete Ressource eingebunden werden muss. Vllt. kennt dazu aber jemand noch nen Trick, damit bei änderungen an den Dlls oder Wechseln Debug/Release/etc immer alles auf dem neuesten Stand kommt.

🙂Anbei die Komplettlösung:

  1. In MainProjekt alle nicht MS Verweise (Dll's) als "Vorhandenes Element..." hinzufügen ("Als Link hinzufügen" reicht aus). (Habs direkt in den Rootpfad, geladen. Andere Pfade sollten auch gehen, aber dann sind im Code (ResolveEventHandler) die Pfade evtl. auch anzupassen)
  2. Jede Dll auf Buildvorgang="Eingebettete Ressource" bei den Eigenschaften umstellen.
  3. In der Program.cs Klasse darf nichts vor der Main an eingebundenen (Verweis) Dll's verwendet werden.
  4. Main Methode in Program.cs wie folgt anpassen:

namespace myTools.Tool
{
    class Program
    { 
        #region Main-Routine

        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += ResolveEventHandler;
            
            Main2();
        }

       // Behandelt die im Ordner nicht gefundenen Dlls und lädt sie über die eingebetteten Ressourcen
        private static Assembly ResolveEventHandler(Object sender, ResolveEventArgs args)
        {
            String dllName = new AssemblyName(args.Name).Name + ".dll";
            var assem = Assembly.GetExecutingAssembly();
            String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
            if (resourceName == null) return null; // Wenn nicht gefunden, wird evtl. ein anderes Handlerereignis diese Ressource auflösen

            using (var stream = assem.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        }

       // Die ursprüngliche Main Methode mit dem bisherigen Code
        private static void Main2()
        {
            myTools.helper.Json myJsonHelper = null;
... (hier kommt weiterer Code)
         }
     } 
}

So ich hoffe das hilft auch allen anderen, die vor diesem Problem, viele Dll ein eine alleinstehende Exe einzubinden stehen.

Grüße Cornflake