Incrustar una DLL dentro de otra como recurso incrustado y luego llamarla desde mi código

StackOverflow https://stackoverflow.com/questions/96732

  •  01-07-2019
  •  | 
  •  

Pregunta

Tengo una situación en la que estoy creando una DLL que utiliza otra DLL de terceros, pero preferiría poder crear la DLL de terceros en mi DLL en lugar de tener que mantener ambas juntas si es posible.

Esto es C# y .NET 3.5.

La forma en que me gustaría hacer esto es almacenando la DLL de terceros como un recurso integrado que luego coloco en el lugar apropiado durante la ejecución de la primera DLL.

La forma en que originalmente planeé hacer esto es escribiendo código para colocar la DLL de terceros en la ubicación especificada por System.Reflection.Assembly.GetExecutingAssembly().Location.ToString() menos el último /nameOfMyAssembly.dll.Puedo salvar con éxito al tercero. .DLL en esta ubicación (que termina siendo

C: Documentos y configuraciones MyUSername Local Settings Application Data Assembly DL3 KXPPAX6Y.ZCY A1MZ1499.1TR E0115D44 91BB86EB_FE18C901

), pero cuando llego a la parte de mi código que requiere esta DLL, no puedo encontrarla.

¿Alguien tiene alguna idea de lo que debo hacer de manera diferente?

¿Fue útil?

Solución

Una vez que haya integrado el ensamblado de terceros como recurso, agregue código para suscribirse al AppDomain.AssemblyResolve evento del dominio actual durante el inicio de la aplicación.Este evento se activa cada vez que el subsistema Fusion de CLR no logra ubicar un ensamblaje de acuerdo con las políticas de sondeo vigentes.En el controlador de eventos para AppDomain.AssemblyResolve, carga el recurso usando Assembly.GetManifestResourceStream e introducir su contenido como una matriz de bytes en el correspondiente Assembly.Load sobrecarga.A continuación se muestra cómo podría verse una implementación de este tipo en 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;
    }
};

dónde StreamToBytes podría definirse como:

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

Finalmente, como algunos ya han mencionado, ILFusionar Puede ser otra opción a considerar, aunque algo más complicada.

Otros consejos

Al final lo hice casi exactamente como sugirió raboof (y similar a lo que sugirió dgvid), excepto con algunos cambios menores y algunas omisiones corregidas.Elegí este método porque era el más cercano a lo que estaba buscando en primer lugar y no requería el uso de ejecutables de terceros ni nada parecido.¡Funciona muy bien!

Así es como terminó luciendo mi código:

EDITAR:Decidí mover esta función a otro ensamblado para poder reutilizarla en varios archivos (simplemente paso Assembly.GetExecutingAssembly()).

Esta es la versión actualizada que le permite pasar el ensamblaje con las DLL integradas.

embedResourcePrefix es la ruta de cadena al recurso incrustado; normalmente será el nombre del ensamblado seguido de cualquier estructura de carpetas que contenga el recurso (p. ej."MyComapny.MyProduct.MyAssembly.Resources" si el dll está en una carpeta llamada Recursos en el proyecto).También se supone que el dll tiene una extensión .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();
        }
    }

Existe una herramienta llamada IlMerge que puede lograr esto: http://research.microsoft.com/~mbarnett/ILMerge.aspx

Luego puedes crear un evento de compilación similar al siguiente.

Establecer ruta="C:\Program Files\Microsoft\ILMerge"

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

Tuve éxito al hacer lo que usted describe, pero debido a que la DLL de terceros también es un ensamblado .NET, nunca la escribo en el disco, simplemente la cargo desde la memoria.

Obtengo el ensamblaje de recursos integrado como una matriz de bytes como esta:

        Assembly resAssembly = Assembly.LoadFile(assemblyPathName);

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

Luego cargo los datos con Assembly.Load().

Finalmente, agrego un controlador a AppDomain.CurrentDomain.AssemblyResolve para devolver mi ensamblado cargado cuando el cargador de tipos lo mira.

Ver el Taller de fusión .NET para detalles adicionales.

Puedes lograr esto muy fácilmente usando netz, un compresor y empaquetador de ejecutables .net NET.

En lugar de escribir el ensamblaje en el disco, puede intentar hacer Assembly.Load(byte[] rawAssembly) donde crea rawAssembly a partir del recurso incrustado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top