Eine DLL in eine andere als eingebettete Ressource einbetten und dann von meinem Code aus aufrufen

Ich habe eine Situation, in der ich eine DLL habe, die ich schreibe, die eine andere DLL eines Drittanbieters benutzt, aber ich würde bevorzugen, die dritte Partei DLL in meine DLL zu bauen, anstatt sie beide zusammen zu behalten, wenn möglich.

Dies ist mit C# und .NET 3.5.

Die Art und Weise, wie ich das machen möchte, besteht darin, die Drittanbieter-DLL als eine eingebettete Ressource zu speichern, die ich dann während der Ausführung der ersten DLL an die entsprechende Stelle lege.

Die ursprünglich geplante Vorgehensweise besteht darin, Code zu schreiben, um die DLL des Drittanbieters an den Speicherort zu setzen, der von System.Reflection.Assembly.GetExecutingAssembly() angegeben wurde. Location.ToString() minus die letzte /nameOfMyAssembly.dll . Ich kann die .DLL von Drittanbietern erfolgreich an diesem Ort speichern (was letztendlich der Fall ist)

C: \ Dokumente und Einstellungen \ meinBenutzername \ Lokale Einstellungen \ Anwendung   Data \ assembly \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901

), aber wenn ich zu dem Teil meines Codes komme, der diese DLL benötigt, kann er sie nicht finden.

Hat jemand eine Idee, was ich anders machen muss?

0

6 Antworten

Nachdem Sie die Assembly von Drittanbietern als Ressource eingebettet haben, fügen Sie Code zum Abonnieren der AppDomain.AssemblyResolve Ereignis der aktuellen Domäne beim Start der Anwendung. Dieses Ereignis wird ausgelöst, wenn das Fusion-Subsystem der CLR eine Assembly nicht gemäß den gültigen Prüfstrategien (Policies) findet. Laden Sie im Ereignishandler für AppDomain.AssemblyResolve die Ressource mithilfe von Assembly.GetManifestResourceStream und füttert seinen Inhalt als Byte-Array in das entsprechende Assembly.Load Überlastung. Im Folgenden sehen Sie, wie eine solche Implementierung in C# aussehen könnte:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
    var resName = args.Name + ".dll";    
    var thisAssembly = Assembly.GetExecutingAssembly();    
    using (var input = thisAssembly.GetManifestResourceStream(resName))
    {
        return input != null 
             ? Assembly.Load(StreamToBytes(input))
             : null;
    }
};

wo StreamToBytes definiert werden könnte als:

static byte[] StreamToBytes(Stream input) 
{
    var capacity = input.CanSeek ? (int) input.Length : 0;
    using (var output = new MemoryStream(capacity))
    {
        int readLength;
        var buffer = new byte[4096];

        do
        {
            readLength = input.Read(buffer, 0, buffer.Length);
            output.Write(buffer, 0, readLength);
        }
        while (readLength != 0);

        return output.ToArray();
    }
}

Schließlich, wie einige bereits erwähnt haben, kann ILMerge eine weitere Option sein, wenn auch etwas komplizierter.

0
hinzugefügt
GetManifestResourceStream? Die Assembly wäre eine stark typisierte Eigenschaft im Namespace * .Properties.Resources.
hinzugefügt der Autor Will, Quelle
Realisiert nach der Veröffentlichung, dass @dgvid mich in der Reaktionszeit geschlagen hat. : P
hinzugefügt der Autor Atif Aziz, Quelle
Das ist glatt, gut gemacht.
hinzugefügt der Autor jcollum, Quelle
Ich habe diesen Code sehr erfolgreich verwendet, um genau das zu tun, was ich wollte. Sehen Sie meinen Beitrag für ein paar kleinere Syntaxauslassungen, die ich behoben habe (nicht genug rep, um diesen zu bearbeiten;)).
hinzugefügt der Autor Lawrence Johnston, Quelle
Warten Sie, so erfordert dies immer noch "Kontrolle" der exe, wie Richters Idee (die diese Antwort" gestohlen "zu haben scheint) legt nahe, dass es passieren muss? Suche nach dem Kommentar beginnend mit 28 Jul 2010 3:14 PM bei diesem Link Ich klicke auf "während der Anwendung Start-up" in der Antwort, oben - das funktioniert nicht/wenn Sie verteilen nur eine DLL und haben keinen Zugriff auf die EXE?
hinzugefügt der Autor ruffin, Quelle
Für diejenigen, die die Assembly-Klasse zuvor noch nicht verwendet haben, benötigen Sie einen mit der System.Reflection; -Anweisung. Es hat mich ein bisschen gebraucht, um herauszufinden, welche Aussage fehlte, also hilft das vielleicht jemandem.
hinzugefügt der Autor Keith, Quelle

Anstatt die Assembly auf die Festplatte zu schreiben, können Sie versuchen, Assembly.Load (byte [] rawAssembly) auszuführen, wo Sie rawAssembly aus der eingebetteten Ressource erstellen.

0
hinzugefügt

Am Ende habe ich es fast genau so gemacht, wie es raboof vorgeschlagen hat (und ähnlich dem, was dgvid vorgeschlagen hat), abgesehen von einigen kleineren Änderungen und einigen behobenen Auslassungen. Ich wählte diese Methode, weil sie am ehesten dem entsprach, wonach ich gesucht hatte und keine ausführbaren Dateien von Dritten benötigte. Es funktioniert super!

So sah mein Code aus:

EDIT: Ich entschied, diese Funktion zu einer anderen Assembly zu verschieben, damit ich es in mehreren Dateien wiederverwenden konnte (ich übergebe Assembly.GetExecutingAssembly ()).

Dies ist die aktualisierte Version, mit der Sie die Assembly mit den eingebetteten DLLs übergeben können.

embeddedResourcePrefix ist der Zeichenfolgenpfad zur eingebetteten Ressource. Normalerweise ist dies der Name der Assembly, gefolgt von einer beliebigen Ordnerstruktur mit der Ressource (z. B. "MyComapny.MyProduct.MyAssembly.Resources", wenn sich die DLL in einem Ordner namens Ressourcen im Projekt befindet ). Außerdem wird davon ausgegangen, dass die DLL eine DLL-Ressourcenerweiterung hat.

   public static void EnableDynamicLoadingForDlls(Assembly assemblyToLoadFrom, string embeddedResourcePrefix) {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {//had to add =>
            try {
                string resName = embeddedResourcePrefix + "." + args.Name.Split(',')[0] + ".dll.resource";
                using (Stream input = assemblyToLoadFrom.GetManifestResourceStream(resName)) {
                    return input != null
                         ? Assembly.Load(StreamToBytes(input))
                         : null;
                }
            } catch (Exception ex) {
                _log.Error("Error dynamically loading dll: " + args.Name, ex);
                return null;
            }
        };//Had to add colon
    }

    private static byte[] StreamToBytes(Stream input) {
        int capacity = input.CanSeek ? (int)input.Length : 0;
        using (MemoryStream output = new MemoryStream(capacity)) {
            int readLength;
            byte[] buffer = new byte[4096];

            do {
                readLength = input.Read(buffer, 0, buffer.Length);//had to change to buffer.Length
                output.Write(buffer, 0, readLength);
            }
            while (readLength != 0);

            return output.ToArray();
        }
    }
0
hinzugefügt
Vielen Dank, dass Sie Ihren endgültigen Code veröffentlicht haben.
hinzugefügt der Autor Rob Ringham, Quelle

Ich hatte Erfolg, was Sie beschreiben, aber weil die DLL von Drittanbietern auch eine .NET Assembly ist, schreibe ich sie nie auf die Festplatte, ich lade sie einfach aus dem Speicher.

Ich erhalte die eingebettete Ressourcen-Assembly als ein Byte-Array wie folgt:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

        byte[] assemblyData;
        using (Stream stream = resAssembly.GetManifestResourceStream(resourceName))
        {
            assemblyData = ReadBytesFromStream(stream);
            stream.Close();
        }

Dann lade ich die Daten mit Assembly.Load ().

Schließlich füge ich einen Handler zu AppDomain.CurrentDomain.AssemblyResolve hinzu, um meine geladene Assembly zurückzugeben, wenn der Typlader sie sieht.

Weitere Informationen finden Sie im .NET Fusion-Workshop .

0
hinzugefügt

There's a tool called IlMerge that can accomplish this: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Dann können Sie einfach ein Build-Ereignis ähnlich dem Folgenden erstellen.

Legen Sie Path = "C: \ Programme \ Microsoft \ ILMerge" fest

ilmerge /out:$(ProjectDir)\Deploy\LevelEditor.exe $ (ProjectDir) \ bin \ Release \ release.exe $ (Projektverzeichnis) \ bin \ Release \ InteractLib.dll $ (ProjectDir) \ bin \ Release \ SpriteLib.dll $ (Projektverzeichnis) \ bin \ Release \ LevelLibrary.dll

0
hinzugefügt

You can achieve this remarkably easily using Netz, a .net NET Executables Compressor & Packer.

0
hinzugefügt