Incorporamento di DLL in un eseguibile compilato


619

È possibile incorporare una DLL preesistente in un eseguibile compilato C # (in modo da disporre di un solo file da distribuire)? Se è possibile, come si farebbe per farlo?

Normalmente, sto bene lasciando semplicemente le DLL fuori e facendo in modo che il programma di installazione gestisca tutto, ma ci sono state un paio di persone al lavoro che mi hanno chiesto questo e sinceramente non lo so.


Ti consiglio di dare un'occhiata all'utilità .NETZ, che comprime anche l'assemblaggio con uno schema a tua scelta: http://madebits.com/netz/help.php#single
Nathan Baulch,

2
È possibile, ma finirai con un eseguibile di grandi dimensioni (Base64 verrà utilizzato per codificare la tua dll).
Paweł Dyda,

Oltre a ILMerge , se non vuoi preoccuparti delle opzioni della riga di comando, consiglio vivamente ILMerge-Gui . È un progetto open source, davvero buono!
Tyron,

2
@ PawełDyda: è possibile incorporare dati binari non elaborati in un'immagine PE (vedere RCDATA ). Nessuna trasformazione richiesta (o consigliata).
Probabile il

Risposte:


761

Consiglio vivamente di utilizzare Costura.Fody - di gran lunga il modo migliore e più semplice per incorporare risorse nel proprio assieme. È disponibile come pacchetto NuGet.

Install-Package Costura.Fody

Dopo averlo aggiunto al progetto, incorporerà automaticamente tutti i riferimenti copiati nella directory di output nell'assieme principale . Potresti voler pulire i file incorporati aggiungendo una destinazione al tuo progetto:

Install-CleanReferencesTarget

Sarai anche in grado di specificare se includere i pdb, escludere determinati assembly o estrarre al volo gli assembly. Per quanto ne so, sono supportati anche gli assembly non gestiti.

Aggiornare

Attualmente, alcune persone stanno cercando di aggiungere supporto per DNX .

Aggiornamento 2

Per l'ultima versione di Fody, dovrai avere MSBuild 16 (quindi Visual Studio 2019). Fody versione 4.2.1 eseguirà MSBuild 15. (riferimento: Fody è supportato solo su MSBuild 16 e versioni successive. Versione corrente: 15 )


79
Grazie per questo fantastico suggerimento. Installa il pacchetto e il gioco è fatto. Comprime persino gli assiemi per impostazione predefinita.
Daniel,

9
Odio essere un 'me stesso', ma anche io - questo mi ha fatto risparmiare un sacco di mal di testa! Grazie per la raccomandazione! Questo mi ha permesso di impacchettare tutto ciò di cui ho bisogno per ridistribuire in un singolo exe ed è ora più piccolo dell'exe originale e le dll sono state combinate ... Lo sto usando solo da alcuni giorni, quindi non posso dire che ' l'ho messo alla prova, ma escludendo qualsiasi cosa brutta saltar fuori, posso vedere che questo diventa uno strumento normale nella mia cassetta degli attrezzi. Funziona e basta!
mattezell,

20
È bello Ma c'è uno svantaggio: l'assemblaggio generato su Windows non è più binario compatibile con mono Linux. Ciò significa che non è possibile distribuire l'assembly direttamente su Linux mono.
Tyler Long,

7
È adorabile! Se stai usando vs2018, non dimenticare il file FodyWeavers.xml che si trova nella radice del tuo progetto.
Alan Deep

4
Come supplemento all'ultimo commento: aggiungi FodyWeavers.xml con il seguente contenuto al tuo progetto: <? Xml version = "1.0" encoding = "utf-8"?> <Weavers VerifyAssembly = "true"> <Costura /> </Weavers>
HHenn,

89

Basta fare clic con il tasto destro del mouse sul progetto in Visual Studio, selezionare Proprietà progetto -> Risorse -> Aggiungi risorsa -> Aggiungi file esistente ... E includere il codice seguente in 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);
}

Ecco il mio post originale sul blog: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/


6
Puoi avere questo comportamento fuori dalla scatola. Scopri la mia risposta stackoverflow.com/a/20306095/568266
Matthias,

4
Anche importante notare un commento INCREDIBILMENTE utile sul tuo blog da AshRowe: se hai un tema personalizzato installato, proverà a risolvere il PresentationFramework. L'assemblaggio che si blocca e brucia! Come suggerito da AshRowe, puoi semplicemente controllare se dllName contiene PresentationFramework in questo modo: if (dllName.ToLower (). Contains ("presentationframework")) return null;
YasharBmanman,

4
Due commenti su questo. Uno: dovresti controllare se bytesè nullo e, in tal caso, restituire null lì. È possibile che la dll non sia nelle risorse, dopo tutto. Due: Funziona solo se quella stessa classe non ha un "utilizzo" per nulla di quell'assemblaggio. Per gli strumenti da riga di comando, ho dovuto spostare il mio attuale codice di programma in un nuovo file e creare un piccolo nuovo programma principale che lo fa e poi chiama il principale originale nella vecchia classe.
Nyerguds,

2
Il vantaggio di questo approccio è che non si basa sull'installazione di librerie esterne per ottenere la funzionalità desiderata. Il rovescio della medaglia di questo approccio è che è utile solo quando si tratta di DLL gestite - interop le dll (almeno per quanto riguarda i miei test) non generano l'evento assemblyresolve e anche se hanno fatto Assembly.Load (<byte di alcune interop .dll>) non ottiene l'effetto desiderabile lungo la strada. stackoverflow.com/questions/13113131/… Solo il mio 2c in materia
XDS

3
Nel caso in cui qualcuno si imbatta nel mio problema: se il .dllnome contiene trattini (es. twenty-two.dll), Anche questi verranno sostituiti da un trattino basso (es twenty_two.dll.). È possibile modificare questa riga di codice in questo:dllName = dllName.Replace(".", "_").Replace("-", "_");
Micah Vertal

87

Se in realtà sono assiemi gestiti, è possibile utilizzare ILMerge . Per le DLL native, avrai un po 'più di lavoro da fare.

Vedi anche: Come si può unire una DLL di Windows C ++ in un exe dell'applicazione C #?


Sono interessato all'unione nativa di DLL, c'è qualche materiale?
Baiyan Huang


@BaiyanHuang guarda github.com/boxedapp/bxilmerge , l'idea è di creare "ILMerge" per le Dll native.
Artem Razin,

Gli sviluppatori di VB NET come me non hanno paura di questo C++al link. ILMerge funziona anche molto facilmente per VB NET. Vedi qui https://github.com/dotnet/ILMerge . Grazie @ Shog9
Ivan Ferrer Villa

26

Sì, è possibile unire gli eseguibili .NET con le librerie. Sono disponibili più strumenti per eseguire il lavoro:

  • ILMerge è un'utilità che può essere utilizzata per unire più assembly .NET in un singolo assembly.
  • Mono mkbundle , confeziona un exe e tutti gli assembly con libmono in un unico pacchetto binario.
  • IL-Repack è un'alternativa FLOSS a ILMerge, con alcune funzionalità aggiuntive.

Inoltre, questo può essere combinato con Mono Linker , che rimuove il codice inutilizzato e quindi riduce l'assemblaggio risultante.

Un'altra possibilità è quella di utilizzare .NETZ , che non solo consente la compressione di un assembly, ma può anche comprimere le dll direttamente nell'esempio. La differenza rispetto alle soluzioni sopra menzionate è che .NETZ non le unisce, rimangono assiemi separati ma sono impacchettati in un unico pacchetto.

.NETZ è uno strumento open source che comprime e comprime i file eseguibili di Microsoft .NET Framework (EXE, DLL) per renderli più piccoli.


NETZ sembra essere sparito
Rbjz il

Wow - Pensavo di averlo finalmente trovato, quindi ho letto questo commento. Sembra essere andato completamente. Ci sono delle forchette?
Mafii,

Bene, è appena passato a GitHub e non è più collegato al sito Web ... quindi "andato totalmente" è un'esagerazione. Molto probabilmente non è più supportato, ma è ancora lì. Ho aggiornato il link.
Bobby,

20

ILMerge può combinare gli assembly in un singolo assembly purché l'assembly abbia gestito solo il codice. È possibile utilizzare l'app a riga di comando o aggiungere riferimento a exe e unire a livello di codice. Per una versione della GUI c'è Eazfuscator e anche .Netz entrambi gratuiti. Le app a pagamento includono BoxedApp e SmartAssembly .

Se dovessi unire gli assembly con codice non gestito, suggerirei SmartAssembly . Non ho mai avuto singhiozzi con SmartAssembly ma con tutti gli altri. Qui, può incorporare le dipendenze richieste come risorse per il tuo exe principale.

Puoi fare tutto questo manualmente senza preoccuparti se l'assembly è gestito o in modalità mista incorporando dll nelle tue risorse e quindi affidandoti all'Assemblea di AppDomain ResolveHandler. Questa è una soluzione completa adottando il caso peggiore, ovvero assemblaggi con codice non gestito.

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        string assemblyName = new AssemblyName(args.Name).Name;
        if (assemblyName.EndsWith(".resources"))
            return null;

        string dllName = assemblyName + ".dll";
        string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);

            //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

            File.WriteAllBytes(dllFullPath, data);
        }

        return Assembly.LoadFrom(dllFullPath);
    };
}

La chiave qui è scrivere i byte in un file e caricarli dalla sua posizione. Per evitare problemi con pollo e uova, è necessario assicurarsi di dichiarare il gestore prima di accedere all'assemblaggio e di non accedere ai membri dell'assieme (o di creare un'istanza di tutto ciò che deve occuparsi dell'assemblaggio) all'interno della parte di caricamento (risoluzione dell'assemblaggio). Assicurati anche di GetMyApplicationSpecificPath()non avere alcuna directory temporanea poiché i file temporanei potrebbero essere tentati di essere cancellati da altri programmi o da te stesso (non che verrà cancellato mentre il tuo programma accede alla dll, ma almeno è un fastidio. AppData è buono Posizione). Si noti inoltre che è necessario scrivere i byte ogni volta, non è possibile caricare dalla posizione solo perché la dll risiede già lì.

Per le DLL gestite, non è necessario scrivere byte, ma caricare direttamente dalla posizione della DLL, o semplicemente leggere i byte e caricare l'assembly dalla memoria. In questo modo:

    using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
    {
        byte[] data = new byte[stream.Length];
        s.Read(data, 0, data.Length);
        return Assembly.Load(data);
    }

    //or just

    return Assembly.LoadFrom(dllFullPath); //if location is known.

Se l'assembly è completamente non gestito, è possibile vedere questo link o questo su come caricare tali dll.


Si noti che "Azione build" della risorsa deve essere impostata su "Risorsa incorporata".
Mavamaarten,

@Mavamaarten Non necessariamente. Se viene aggiunto in anticipo a Resources.resx del progetto, non è necessario farlo.
Nyerguds,

2
EAZfuscator è ora commerciale.
Telemat,

16

L' estratto di Jeffrey Richter è molto buono. In breve, aggiungi la libreria come risorse incorporate e aggiungi un callback prima di ogni altra cosa. Ecco una versione del codice (trovata nei commenti della sua pagina) che ho messo all'inizio del metodo Main per un'app console (assicurati solo che tutte le chiamate che usano la libreria siano in un metodo diverso da 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);
            }
        };

1
L'ho cambiato un po ', fatto il lavoro, grazie amico!
Sean Ed-Man,

Il progetto libz.codeplex.com utilizza questo processo, ma farà anche altre cose come gestire il gestore di eventi per te e un codice speciale per non rompere i " cataloghi Managed Extensibility Framework " (che di per sé questo processo si spezzerebbe)
Scott Chamberlain,

È fantastico!! Grazie @Steve
Ahmer Afzal

14

Per espandere su asnwer @ Bobby sopra. Puoi modificare il tuo .csproj per usare IL-Repack per impacchettare automaticamente tutti i file in un singolo assembly durante la compilazione.

  1. Installare il pacchetto nuget ILRepack.MSBuild.Task con Install-Package ILRepack.MSBuild.Task
  2. Modifica la sezione AfterBuild del tuo .csproj

Ecco un semplice esempio che unisce EsempioAssemblyToMerge.dll all'output del progetto.

<!-- 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>

1
La sintassi di IL-Repack è cambiata, controllare README.md presente nel repository github collegato ( github.com/peters/ILRepack.MSBuild.Task ). In questo modo è stato l'unico che ha funzionato per me e sono stato in grado di utilizzare un carattere jolly per abbinare tutte le DLL che volevo includere.
Seabass77,

8

È possibile aggiungere le DLL come risorse incorporate, quindi fare in modo che il programma le scompigli nella directory dell'applicazione all'avvio (dopo aver verificato che siano già presenti).

I file di installazione sono così facili da realizzare, tuttavia, che non credo ne varrebbe la pena.

EDIT: questa tecnica sarebbe facile con gli assembly .NET. Con DLL non.NET sarebbe molto più lavoro (dovresti capire dove decomprimere i file e registrarli e così via).


Ecco un ottimo articolo che spiega come fare: codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource
bluish

8

Un altro prodotto in grado di gestirlo in modo elegante è SmartAssembly, su SmartAssembly.com . Questo prodotto, oltre a fondere tutte le dipendenze in una singola DLL, (facoltativamente) offusca il codice, rimuove i metadati aggiuntivi per ridurre le dimensioni del file risultante e può anche effettivamente ottimizzare l'IL per aumentare le prestazioni di runtime.

Esiste anche una sorta di funzionalità globale di gestione / reporting delle eccezioni che aggiunge al software (se lo si desidera) che potrebbe essere utile. Credo che abbia anche un'API della riga di comando, quindi puoi renderla parte del tuo processo di compilazione.


7

Né l'approccio ILMerge né la gestione di Lars Holm Jensen dell'evento AssemblyResolve funzioneranno per un host plugin. Dire che l'eseguibile H carica l'assemblaggio P in modo dinamico e accede ad esso tramite l'interfaccia IP definita in un assieme separato. Per incorporare IP in H è necessario apportare una piccola modifica al codice di 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;
};  

Il trucco per gestire ripetuti tentativi di risolvere lo stesso assembly e restituire quello esistente invece di creare una nuova istanza.

EDIT: per non rovinare la serializzazione di .NET, assicurati di restituire null per tutti gli assembly non incorporati nel tuo, in modo che il comportamento standard sia predefinito. È possibile ottenere un elenco di queste librerie:

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

e restituisce null se l'assembly passato non appartiene IncludedAssemblies.


Ci scusiamo per averlo pubblicato come risposta piuttosto che come commento. Non ho il diritto di commentare le risposte degli altri.
Ant_222,

5

.NET Core 3.0 supporta nativamente la compilazione in un singolo .exe

La funzione è abilitata dall'uso della seguente proprietà nel file di progetto (.csproj):

    <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>

Questo viene fatto senza alcun strumento esterno.

Vedi la mia risposta a questa domanda per ulteriori dettagli.


3

Può sembrare semplicistico, ma WinRar offre la possibilità di comprimere un mucchio di file in un eseguibile autoestraente.
Ha molte opzioni configurabili: icona finale, estrarre i file nel percorso specificato, file da eseguire dopo l'estrazione, logo / testi personalizzati per i popup visualizzati durante l'estrazione, nessuna finestra popup, testo dell'accordo di licenza, ecc.
Può essere utile in alcuni casi .


Windows stesso ha uno strumento simile chiamato iexpress. Ecco un tutorial
Ivan Ferrer Villa,

2

Uso il compilatore csc.exe chiamato da uno script .vbs.

Nel tuo script xyz.cs, aggiungi le seguenti righe dopo le direttive (il mio esempio è per 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"

I tag ref, res e ico saranno raccolti dallo script .vbs in basso per formare il comando csc.

Quindi aggiungere il chiamante del resolver dell'assembly nel Main:

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

... e aggiungi il risolutore stesso da qualche parte nella classe:

    assembly statico CurrentDomain_AssemblyResolve (mittente oggetto, argomenti ResolveEventArgs)
    {
        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);
        }

    }

Il nome dello script vbs corrisponde al nome file .cs (ad es. Ssh.vbs cerca ssh.cs); questo rende l'esecuzione dello script molte volte molto più semplice, ma se non sei un idiota come me, uno script generico potrebbe raccogliere il file .cs di destinazione da un trascinamento della selezione:

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

    'TRATTARE IL NOME SCRIPT VBS COME NOME FILE TARGET
    '################################################
    name_ = Split (wscript.ScriptName, ".") (0)

    'OTTIENI I NOME DELLE ICONE E DELLE ICONE ESTERNE DAL FILE .CS
    '################################################# ######
    Const OPEN_FILE_FOR_READING = 1
    Set objInputFile = fso.OpenTextFile (name_ & ".cs", 1)

    'LEGGI TUTTO IN UN ARRAY
    '#############################
    inputData = Split (objInputFile.ReadAll, vbNewline)

    Per ogni strData In inputData

        se lasciato (strData, 7) = "// + ref>" allora 
            csc_references = csc_references & "/ reference:" & trim (replace (strData, "// + ref>", "")) & ""
        finisci se

        se lasciato (strData, 7) = "// + res>" allora 
            csc_resources = csc_resources & "/ resource:" & trim (replace (strData, "// + res>", "")) & ""
        finisci se

        se lasciato (strData, 7) = "// + ico>" allora 
            csc_icon = "/ win32icon:" & trim (sostituisci (strData, "// + ico>", "")) & ""
        finisci se
    Il prossimo

    objInputFile.Close


    'COMPILA IL 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)

0

È possibile, ma non è così semplice, creare un assembly ibrido nativo / gestito in C #. Se si usasse C ++ invece sarebbe molto più semplice, poiché il compilatore Visual C ++ può creare assiemi ibridi con la stessa facilità di qualsiasi altra cosa.

A meno che tu non abbia un requisito rigoroso per produrre un assemblaggio ibrido, sarei d'accordo con MusiGenesis che questo non vale davvero la pena avere a che fare con C #. Se devi farlo, forse guarda invece al passaggio a C ++ / CLI.


0

In genere, è necessario disporre di una qualche forma di strumento di post-creazione per eseguire una fusione di assiemi come si sta descrivendo. Esiste uno strumento gratuito chiamato Eazfuscator (eazfuscator.blogspot.com/) progettato per la manipolazione dei bytecode che gestisce anche l'unione degli assiemi. Puoi aggiungerlo in una riga di comando post build con Visual Studio per unire i tuoi assiemi, ma il tuo chilometraggio varierà a causa di problemi che sorgeranno in qualsiasi scenario di fusione di assiemi non trivalenti.

Potresti anche verificare se la build rende disordinato NANT ha la capacità di unire gli assembly dopo la costruzione, ma non ho abbastanza familiarità con NANT stesso per dire se la funzionalità è integrata o meno.

Esistono anche molti plug-in di Visual Studio che eseguiranno l'unione dell'assemblaggio come parte della creazione dell'applicazione.

In alternativa, se non è necessario che ciò avvenga automaticamente, esistono numerosi strumenti come ILMerge che uniranno gli assembly .net in un singolo file.

Il problema più grande che ho avuto con l'unione di assembly è se usano spazi dei nomi simili. O peggio, fare riferimento a versioni diverse della stessa dll (i miei problemi erano generalmente con i file dll NUnit).


1
Eazfuscator chiamerà semplicemente IlMerge, AFAIK.
Bobby,

+1 Bobby. Avrei dovuto ricordarmelo. Tutto ciò che Eazfucator fa per te è astratto delle chiamate effettive a ILMerge con un file di configurazione più generale.
wllmsaccnt,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.