2008-09-18 23 views
53

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 mantenerlos juntos si es posible.Incrustar un dll dentro de otro como un recurso incrustado y luego llamarlo desde mi código

Este con es C# y .NET 3.5.

La manera 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 la DLL de terceros en la ubicación especificada por System.Reflection.Assembly.GetExecutingAssembly(). Location.ToString() menos la última /nameOfMyAssembly.dll. Puedo guardar con éxito el .DLL de terceros en esta ubicación (que termina siendo (C: \ Documents and Settings \ 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 puede encontrarlo.

¿alguien tiene alguna idea de lo que tengo que estar haciendo de manera diferente?

Respuesta

41

Una vez que haya incrustado el ensamblado de terceros como un recurso, agregue el código para suscribirse al evento AppDomain.AssemblyResolve 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 alimente su contenido como una matriz de bytes en la correspondiente sobrecarga Assembly.Load.A continuación se muestra cómo una tal aplicación podría ser similar 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 se podría definir 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(); 
    } 
} 

Por último, como algunos ya han mencionado, ILMerge puede ser otra opción a considerar, aunque algo más involucrado.

+0

Se dio cuenta después de publicar que @dgvid me ganó en el tiempo de respuesta. : P –

+0

Usé 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;)). –

+0

Eso es resbaladizo, bien hecho. – jcollum

11

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

a continuación, puedes realizar un evento de construcción similar a la 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

2

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

9

Puede lograr esto notablemente fácilmente usando Netz,. NET NET Executables Compressor & Packer.

8

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

recibo el ensamblado de recursos incrustado como una matriz de bytes de este modo:

 Assembly resAssembly = Assembly.LoadFile(assemblyPathName); 

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

Entonces cargar los datos con Assembly.Load().

Finalmente, agregué un controlador a AppDomain.CurrentDomain.AssemblyResolve para devolver mi ensamblado cargado cuando el cargador de tipo lo busca.

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

17

Al final lo hice casi exactamente como lo 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 ningún archivo ejecutable de terceros y tal. ¡Funciona genial!

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

EDIT: decidí cambiar esta función a otra asamblea para que pudiera volver a utilizarlo en varios archivos (acabo de pasar en Assembly.GetExecutingAssembly()).

Esta es la versión actualizada que le permite pasar el ensamblaje 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 Recursos en el proyecto). 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

¡Gracias por publicar su código final, puedo terminar usando eso en alguna parte! –

Cuestiones relacionadas