Încărcarea unui DLL în altul ca resursă integrată și apoi apelarea din codul meu

Am o situație în care am un DLL pe care îl creez și care folosește un DLL de la o altă parte, dar aș prefera să pot construi DLL-ul terță parte în DLL-ul meu, în loc să trebuiască să-i păstrez pe amândouă împreună, dacă este posibil.

Aceasta este cu C# și .NET 3.5.

Modul în care aș dori să fac acest lucru este prin stocarea DLL-ul terț ca o resursă încorporată pe care apoi o loc în locul potrivit în timpul executării primului DLL.

Modul în care am intenționat inițial să fac acest lucru este scrierea unui cod pentru a pune DLL-ul terț în locația specificată de System.Reflection.Assembly.GetExecutingAssembly (). Location.ToString() minus ultimul /nameOfMyAssembly.dll . Pot salva cu succes numele terț .DLL în această locație (care sfârșește prin a fi

C: \ Documents and Settings \ myUserName \ Local Settings \ Aplicație   Datele \ asamblare \ dl3 \ KXPPAX6Y.ZCY \ A1MZ1499.1TR \ e0115d44 \ 91bb86eb_fe18c901

), dar când ajung la partea din codul meu care necesită acest DLL, nu se poate găsi.

Are cineva vreo idee despre ceea ce trebuie să fac diferit?

0

6 răspunsuri

După ce ați încorporat ansamblul terță parte ca resursă, adăugați codul pentru a vă abona la AppDomain.AssemblyResolve eveniment al domeniului curent în timpul pornirii aplicației. Acest eveniment se declanșează ori de câte ori subsistemul Fusion al CLR nu reușește să găsească un ansamblu în conformitate cu politicile în vigoare. În procedura handler pentru AppDomain.AssemblyResolve , încărcați resursele utilizând Assembly.GetManifestResourceStream și să-i hrăniți conținutul ca o matrice de octeți în Adunarea.Load supraîncărcare. Mai jos este modul în care o astfel de implementare ar putea arăta în C #:

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

unde StreamToBytes ar putea fi definite ca:

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

În cele din urmă, după cum am menționat deja, ILMerge poate fi o altă opțiune de analizat, deși mai puțin implicat.

0
adăugat
GetManifestResourceStream? Ansamblul ar fi o proprietate puternic tipărită sub spațiul de nume * .Properties.Resources.
adăugat autor Will, sursa
Realizat după ce am postat că @dgvid mi-a bătut în timpul de răspuns. : P
adăugat autor Atif Aziz, sursa
Asta este slick, bine făcut.
adăugat autor jcollum, sursa
Am folosit foarte mult acest cod pentru a face exact ceea ce vroiam. Vedeți postarea mea pentru câteva din omisiunile de sintaxă minore pe care le-am stabilit (nu este suficient rep pentru a edita această;)).
adăugat autor Lawrence Johnston, sursa
Pentru cei care nu au folosit clasa Adunării înainte, trebuie să aveți o instrucțiune utilizând instrucțiunea System.Reflection; . Mi-a luat un pic să-mi dau seama care a fost lipsa unei declarații, așa că poate că asta va ajuta pe cineva.
adăugat autor Keith, sursa

În loc să scrieți ansamblul pe disc, puteți încerca să faceți Assembly.Load (byte [] rawAssembly) unde creați rawAssembly din resursa încorporată.

0
adăugat

În cele din urmă, am făcut-o exact așa cum sugera robotul (și similar cu ceea ce sugera dgvid), cu excepția unor modificări minore și a unor omisiuni fixate. Am ales această metodă pentru că era mai aproape de ceea ce căutam în primul rând și nu am solicitat utilizarea unor executabile terțe părți și altele asemenea. Funcționează minunat!

Acesta este modul în care codul meu a ajuns să arate ca:

EDIT: Am decis să mut această funcție într-un alt ansamblu, așa că am putea reutiliza în mai multe fișiere (am trecut doar în Assembly.GetExecutingAssembly ()).

Aceasta este versiunea actualizată care vă permite să treceți în ansamblu cu dll-urile încorporate.

embeddedResourcePrefix este calea de șir pentru resursa încorporată, va fi de obicei numele ansamblului urmat de orice structură de folder care conține resursa (de ex. "MyComapny.MyProduct.MyAssembly.Resources" dacă dll-ul se află într-un director numit Resurse în proiect ). De asemenea, presupune că DLL are o extensie .dll.resource.

   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
adăugat
Vă mulțumim că ați postat codul final, că s-ar putea să-l folosesc pe undeva!
adăugat autor Rob Ringham, sursa

Am avut succes în a face ceea ce descrieți, dar deoarece DLL-ul terț este de asemenea un ansamblu .NET, nu l-am scris niciodată pe disc, tocmai l-am încărcat din memorie.

Obținem ansamblul resurselor integrate ca o matrice de octeți, cum ar fi:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

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

Apoi încărc datele cu Assembly.Load ().

În cele din urmă, adaug un handler la AppDomain.CurrentDomain.AssemblyResolve pentru a reveni la ansamblul meu încărcat atunci când tipul de încărcător îl privește.

Consultați atelierul Fusion Fusion pentru detalii suplimentare.

0
adăugat

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

Apoi puteți face doar un eveniment de construcție similar cu cele de mai jos.

Setați calea = "C: \ Program Files \ Microsoft \ ILMerge"

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

0
adăugat

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

0
adăugat