c# include - Incrustar DLLs en un ejecutable compilado




exe embed (17)

Yo uso el compilador csc.exe llamado desde un script .vbs.

En su script xyz.cs, agregue las siguientes líneas después de las directivas (mi ejemplo es para Renci SSH):

using System;
using Renci;//FOR THE SSH
using System.Net;//FOR THE ADDRESS TRANSLATION
using System.Reflection;//FOR THE Assembly

//+ref>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+res>"C:\Program Files (x86)\Microsoft\ILMerge\Renci.SshNet.dll"
//+ico>"C:\Program Files (x86)\Microsoft CAPICOM 2.1.0.2 SDK\Samples\c_sharp\xmldsig\resources\Traffic.ico"

Las etiquetas ref, res e ico serán recogidas por el script .vbs a continuación para formar el comando csc.

Luego agregue el ensamblador que llama al que llama en el Principal:

public static void Main(string[] args)
{
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    .

... y agregue el resolutor en algún lugar de la clase:

    static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        String resourceName = new AssemblyName(args.Name).Name + ".dll";

        using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
        {
            Byte[] assemblyData = new Byte[stream.Length];
            stream.Read(assemblyData, 0, assemblyData.Length);
            return Assembly.Load(assemblyData);
        }

    }

Nombro el script vbs para que coincida con el nombre de archivo .cs (por ejemplo, ssh.vbs busca ssh.cs); esto hace que ejecutar el script muchas veces sea mucho más fácil, pero si no eres un idiota como yo, un script genérico podría recoger el archivo .cs de destino arrastrando y soltando:

    Dim name_,oShell,fso
    Set oShell = CreateObject("Shell.Application")
    Set fso = CreateObject("Scripting.fileSystemObject")

    'TAKE THE VBS SCRIPT NAME AS THE TARGET FILE NAME
    '################################################
    name_ = Split(wscript.ScriptName, ".")(0)

    'GET THE EXTERNAL DLL's AND ICON NAMES FROM THE .CS FILE
    '#######################################################
    Const OPEN_FILE_FOR_READING = 1
    Set objInputFile = fso.OpenTextFile(name_ & ".cs", 1)

    'READ EVERYTHING INTO AN ARRAY
    '#############################
    inputData = Split(objInputFile.ReadAll, vbNewline)

    For each strData In inputData

        if left(strData,7)="//+ref>" then 
            csc_references = csc_references & " /reference:" &         trim(replace(strData,"//+ref>","")) & " "
        end if

        if left(strData,7)="//+res>" then 
            csc_resources = csc_resources & " /resource:" & trim(replace(strData,"//+res>","")) & " "
        end if

        if left(strData,7)="//+ico>" then 
            csc_icon = " /win32icon:" & trim(replace(strData,"//+ico>","")) & " "
        end if
    Next

    objInputFile.Close


    'COMPILE THE FILE
    '################
    oShell.ShellExecute "c:\windows\microsoft.net\framework\v3.5\csc.exe", "/warn:1 /target:exe " & csc_references & csc_resources & csc_icon & " " & name_ & ".cs", "", "runas", 2


    WScript.Quit(0)

Sabes, no he visto una buena respuesta para esto en ninguna parte. ¿Es posible incrustar una DLL preexistente en un ejecutable compilado de C # (para que solo tenga un archivo para distribuir)? Si es posible, ¿cómo se haría para hacerlo?

Normalmente, estoy bien con solo dejar las DLL fuera y que el programa de instalación maneje todo, pero ha habido un par de personas en el trabajo que me han preguntado esto y, sinceramente, no lo sé.


Ni el enfoque de ILMerge ni Lars Holm Jensen manejando el evento AssemblyResolve funcionarán para un host de complemento. Digamos que el ejecutable H carga el conjunto P dinámicamente y accede a él a través de la interfaz IP definida en un conjunto separado. Para incrustar IP en H uno necesitará una pequeña modificación al código de Lars:

Dictionary<string, Assembly> loaded = new Dictionary<string,Assembly>();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{   Assembly resAssembly;
    string dllName = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");
    dllName = dllName.Replace(".", "_");
    if ( !loaded.ContainsKey( dllName ) )
    {   if (dllName.EndsWith("_resources")) return null;
        System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());
        byte[] bytes = (byte[])rm.GetObject(dllName);
        resAssembly = System.Reflection.Assembly.Load(bytes);
        loaded.Add(dllName, resAssembly);
    }
    else
    {   resAssembly = loaded[dllName];  }
    return resAssembly;
};  

El truco para manejar intentos repetidos de resolver el mismo ensamblaje y devolver el existente en lugar de crear una nueva instancia.

EDITAR: Para que no estropee la serialización de .NET, asegúrese de devolver el valor nulo para todos los ensamblajes no incrustados en el suyo, por lo que el comportamiento estándar es predeterminado. Puede obtener una lista de estas bibliotecas por:

static HashSet<string> IncludedAssemblies = new HashSet<string>();
string[] resources = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceNames();
for(int i = 0; i < resources.Length; i++)
{   IncludedAssemblies.Add(resources[i]);  }

y simplemente devuelva nulo si el ensamblaje pasado no pertenece a IncludedAssemblies .


Marcar BoxedApp

Puede incrustar un dll en cualquier aplicación. Escrito en C # también, por supuesto :)

Espero eso ayude.


Para expandir en la respuesta de @ Bobby arriba. Puede editar su .csproj para usar IL-Repack para empaquetar automáticamente todos los archivos en un solo ensamblaje cuando construya.

  1. Instale el paquete nuget ILRepack.MSBuild.Task con Install-Package ILRepack.MSBuild.Task
  2. Edita la sección AfterBuild de tu .csproj

Aquí hay una muestra simple que combina ExampleAssemblyToMerge.dll en la salida de su proyecto.

<!-- ILRepack -->
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">

   <ItemGroup>
    <InputAssemblies Include="$(OutputPath)\$(AssemblyName).exe" />
    <InputAssemblies Include="$(OutputPath)\ExampleAssemblyToMerge.dll" />
   </ItemGroup>

   <ILRepack 
    Parallel="true"
    Internalize="true"
    InputAssemblies="@(InputAssemblies)"
    TargetKind="Exe"
    OutputFile="$(OutputPath)\$(AssemblyName).exe"
   />
</Target>

Puede sonar simplista, pero WinRar ofrece la opción de comprimir un montón de archivos en un ejecutable autoextraíble.
Tiene muchas opciones configurables: ícono final, extraer archivos a una ruta determinada, archivo para ejecutar después de la extracción, logotipo / textos personalizados para la ventana emergente que se muestra durante la extracción, ninguna ventana emergente, texto del acuerdo de licencia, etc.
Puede ser útil en algunos casos.


Puede agregar los DLL como recursos incrustados y luego hacer que su programa los descomprima en el directorio de la aplicación al inicio (después de verificar si ya están allí).

Sin embargo, los archivos de configuración son tan fáciles de hacer que no creo que valga la pena.

EDITAR: Esta técnica sería fácil con los ensamblados .NET. Con las DLL que no sean de .NET sería mucho más trabajo (tendría que averiguar dónde desempaquetar los archivos y registrarlos, etc.).


El extracto de Jeffrey Richter es muy bueno. En resumen, agregue la biblioteca como recursos incrustados y agregue una devolución de llamada antes que nada. Aquí hay una versión del código (que se encuentra en los comentarios de su página) que puse al comienzo del método Main para una aplicación de consola (solo asegúrese de que todas las llamadas que usen la biblioteca estén en un método diferente al de Main).

AppDomain.CurrentDomain.AssemblyResolve += (sender, bargs) =>
        {
            String dllName = new AssemblyName(bargs.Name).Name + ".dll";
            var assem = Assembly.GetExecutingAssembly();
            String resourceName = assem.GetManifestResourceNames().FirstOrDefault(rn => rn.EndsWith(dllName));
            if (resourceName == null) return null; // Not found, maybe another handler will find it
            using (var stream = assem.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        };



ILMerge hace exactamente lo que quieres.


Es posible, pero no tan fácil, crear un ensamblaje nativo / administrado híbrido en C #. Si estuvieras usando C ++, sería mucho más fácil, ya que el compilador de Visual C ++ puede crear ensamblajes híbridos tan fácilmente como cualquier otra cosa.

A menos que tenga un requisito estricto para producir un ensamblaje híbrido, estaría de acuerdo con MusiGenesis en que esto no vale la pena hacer con C #. Si necesita hacerlo, quizás busque cambiarse a C ++ / CLI.


Recomiendo usar Costura.Fody - por mucho, la mejor y más sencilla forma de incrustar recursos en su ensamblaje. Está disponible como paquete NuGet.

Install-Package Costura.Fody

Después de agregarlo al proyecto, automáticamente incrustará todas las referencias que se copian en el directorio de salida en su ensamblaje principal . Es posible que desee limpiar los archivos incrustados agregando un objetivo a su proyecto:

Install-CleanReferencesTarget

También podrá especificar si desea incluir los pdb, excluir ciertos ensamblajes o extraer los ensamblajes sobre la marcha. Por lo que yo sé, también son compatibles los ensamblados no administrados.

Actualizar

Actualmente, algunas personas están tratando de agregar soporte para DNX .


Otro producto que puede manejar esto con elegancia es SmartAssembly, en SmartAssembly.com . Este producto, además de fusionar todas las dependencias en una sola DLL, (opcionalmente) ofuscará su código, eliminará metadatos adicionales para reducir el tamaño del archivo resultante y también puede optimizar el IL para aumentar el rendimiento en tiempo de ejecución. También hay algún tipo de característica de gestión / informe de excepciones global que se agrega a su software (si se desea) que no me tomé el tiempo de entender, pero podría ser útil. Creo que también tiene una API de línea de comandos para que puedas hacerla parte de tu proceso de compilación.



Sí, es posible combinar .NET ejecutables con bibliotecas. Hay múltiples herramientas disponibles para hacer el trabajo:

  • ILMerge es una utilidad que se puede usar para combinar varios ensamblados .NET en un solo ensamblaje.
  • Mono mkbundle , empaqueta un exe y todos los ensamblajes con libmono en un solo paquete binario.
  • IL-Repack es un alterante FLOSS para ILMerge, con algunas características adicionales.

Además, esto se puede combinar con el Mono Linker , que elimina el código no utilizado y, por lo tanto, hace que el ensamblaje resultante sea más pequeño.

Otra posibilidad es usar .NETZ , que no solo permite comprimir un ensamblaje, sino que también puede empaquetar las DLL directamente en el .NETZ . La diferencia con las soluciones mencionadas anteriormente es que .NETZ no las combina, se quedan en ensamblajes separados pero se empaquetan en un solo paquete.

.NETZ es una herramienta de código abierto que comprime y empaqueta los archivos ejecutables de Microsoft .NET Framework (EXE, DLL) para hacerlos más pequeños.


Simplemente haga clic con el botón derecho en su proyecto en Visual Studio, elija Propiedades del proyecto -> Recursos -> Agregar recurso -> Agregar archivo existente ... e incluya el siguiente código en su App.xaml.cs o equivalente.

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");

    dllName = dllName.Replace(".", "_");

    if (dllName.EndsWith("_resources")) return null;

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());

    byte[] bytes = (byte[])rm.GetObject(dllName);

    return System.Reflection.Assembly.Load(bytes);
}

Aquí está mi blog original: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/


Si la tabla Dispositivo tiene una clave primaria compuesta, entonces necesita la misma clave externa compuesta en su tabla NotificationMessageDevice . ¿Cómo encontraría SQL un dispositivo sin la clave primaria completa? También debe hacer que estos campos sean parte de la clave primaria de la tabla NotificationMessageDevice . De lo contrario, no puede garantizar que la clave principal sea única:

public class NotificationMessageDevice
{
    [Column(Order = 0), Key, ForeignKey("NotificationMessage")]
    public int NotificationMessage_ID { get; set; }

    [Column(Order = 1), Key, ForeignKey("Device")]
    public int Device_ID { get; set; }
    [Column(Order = 2), Key, ForeignKey("Device")]
    public string Device_UDID { get; set; }
    [Column(Order = 3), Key, ForeignKey("Device")]
    public string Device_ApplicationKey { get; set; }

    public virtual Device Device { get; set; }
    public virtual NotificationMessage NotificationMessage { get; set; }
}




c# .net dll merge linker