Incrustar un dll dentro de otro como un recurso incrustado y luego llamarlo desde mi código

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

Esto con C# y .NET 3.5.

La forma en que me gustaría hacer esto es almacenando la DLL de terceros como un recurso incrustado que 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 poner el DLL de terceros en la ubicación especificada por System.Reflection.Assembly.GetExecutingAssembly (). Location.ToString() menos el último /nameOfMyAssembly.dll . Puedo guardar con éxito el tercero .DLL en esta ubicación (que termina siendo

C: \ Documents and Settings \ myUserName \ Configuración local \ Aplicación   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 puede encontrarla.

¿Alguien tiene alguna idea de lo que necesito hacer diferente?

0
agregado editado
Puntos de vista: 1

6 Respuestas

Una vez que haya incrustado el ensamblado de terceros como un recurso, agregue código para suscribirse al AppDomain.AssemblyResolve evento del dominio actual durante el inicio de la aplicación. Este evento se activa siempre que el subsistema Fusion del CLR no puede ubicar un ensamblaje de acuerdo con las pruebas (políticas) en vigencia. En el controlador de eventos para AppDomain.AssemblyResolve , cargue el recurso usando Assembly.GetManifestResourceStream y alimentar su contenido como una matriz de bytes en el Assembly.Load sobrecarga. A continuación se muestra cómo se vería 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;
    }
};

donde 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, ILMerge puede ser otra opción a considerar, aunque algo más involucrado.

0
agregado
GetManifestResourceStream? El ensamblado sería una propiedad fuertemente tipada en el espacio de nombres * .Properties.Resources.
agregado el autor Will, fuente
Se dio cuenta después de publicar que @dgvid me ganó en tiempo de respuesta. :PAG
agregado el autor Atif Aziz, fuente
Eso es astuto, bien hecho.
agregado el autor jcollum, fuente
Utilicé este código con mucho éxito para hacer exactamente lo que quería. Ver mi publicación para un par de las omisiones menores de sintaxis que arreglé (no hay suficientes representantes para editar ésta;)).
agregado el autor Lawrence Johnston, fuente
Espera, así que esto todavía requiere el "control" del exe, como idea de Richter (que parece haber" robado "esta respuesta) sugiere que tiene que suceder? busque el comentario que comienza con el 28 de julio de 2010 a las 3:14 PM en ese enlace Estoy escuchando "durante la puesta en marcha de la aplicación" en la respuesta anterior: esto funciona/no funciona cuando ¿solo distribuye un dll y no tiene acceso al exe?
agregado el autor ruffin, fuente
Para aquellos que no han usado la clase Assembly anteriormente, debe tener una instrucción using System.Reflection; . Me llevó un poco descubrir qué declaración de uso faltaba, así que tal vez esto ayude a alguien.
agregado el autor Keith, fuente

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

0
agregado

Al final lo hice casi exactamente como sugirió Raboof (y similar a lo que sugería 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 ningún archivo ejecutable de terceros y tal. ¡Funciona genial!

Esto es a lo que mi código terminó pareciéndose:

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

Esta es la versión actualizada que le permite pasar el ensamblado con los dlls incorporados.

embeddedResourcePrefix es la ruta de acceso de cadena al recurso incrustado, normalmente será el nombre del ensamblado seguido de cualquier estructura de carpeta que contenga el recurso (por ejemplo, "MyComapny.MyProduct.MyAssembly.Resources" si el dll está en una carpeta llamada Resources in the project ) También asume 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();
        }
    }
0
agregado
Gracias por publicar su código final, ¡puedo terminar usando eso en alguna parte!
agregado el autor Rob Ringham, fuente

He tenido éxito haciendo lo que describes, pero como el archivo DLL de terceros también es un ensamblado .NET, nunca lo escribo en el disco, solo lo cargo de memoria.

Obtengo el ensamblaje de recursos incrustado 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, agregué un controlador a AppDomain.CurrentDomain.AssemblyResolve para devolver mi ensamblado cargado cuando el cargador de tipos lo busca.

Consulte el .NET Fusion Workshop para obtener más detalles.

0
agregado

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

Entonces puedes hacer un evento de compilación similar al siguiente.

Establecer ruta = "C: \ Archivos de programa \ Microsoft \ ILMerge"

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

0
agregado

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

0
agregado