c# - संकलित निष्पादन योग्य में डीएलएल एम्बेड करना




.net dll (13)

आप जानते हैं, मैंने इसे कहीं भी एक अच्छा जवाब नहीं देखा है। क्या एक पूर्व-मौजूदा DLL को संकलित C # निष्पादन योग्य में एम्बेड करना संभव है (ताकि आपके पास केवल एक फ़ाइल वितरित हो)? यदि यह संभव है, तो कोई इसे करने के बारे में कैसे जाएगा?

आम तौर पर, मैं सिर्फ डीएलएल को बाहर छोड़कर और सेटअप प्रोग्राम को सबकुछ संभालने के साथ अच्छा हूं, लेकिन काम पर कुछ लोग रहे हैं जिन्होंने मुझसे यह पूछा है और मैं ईमानदारी से नहीं जानता।


SmartAssembly.com पर, एक और उत्पाद जो इसे सुंदर ढंग से संभाल सकता है SmartAssembly.com । यह उत्पाद, सभी निर्भरताओं को एक एकल डीएलएल में विलय करने के अलावा, (वैकल्पिक रूप से) आपके कोड को खराब कर देगा, परिणामी फ़ाइल आकार को कम करने के लिए अतिरिक्त मेटा-डेटा हटा देगा, और वास्तव में रनटाइम प्रदर्शन को बढ़ाने के लिए आईएल को अनुकूलित भी कर सकता है। कुछ प्रकार की वैश्विक अपवाद हैंडलिंग / रिपोर्टिंग सुविधा भी आपके सॉफ़्टवेयर में जोड़ती है (यदि वांछित है) कि मैंने समझने का समय नहीं लिया, लेकिन उपयोगी हो सकता है। मेरा मानना ​​है कि इसमें कमांड लाइन एपीआई भी है ताकि आप इसे अपनी बिल्ड प्रक्रिया का हिस्सा बना सकें।


आप डीएलएल को एम्बेडेड संसाधनों के रूप में जोड़ सकते हैं, और फिर अपने प्रोग्राम को स्टार्टअप पर एप्लिकेशन निर्देशिका में अनपैक कर सकते हैं (यह देखने के बाद कि वे पहले से मौजूद हैं या नहीं)।

सेटअप फाइलें बनाना इतना आसान है, हालांकि, मुझे नहीं लगता कि यह इसके लायक होगा।

संपादित करें: यह तकनीक .NET असेंबली के साथ आसान होगी। गैर-एनईटी डीएलएल के साथ यह बहुत अधिक काम करेगा (आपको यह पता लगाना होगा कि फाइलों को अनपैक करना है और उन्हें पंजीकृत करना है)।


उपरोक्त @ बॉबी के एवरवर पर विस्तार करने के लिए। जब आप बिल्ड करते हैं तो आप सभी फ़ाइलों को एक ही असेंबली में स्वचालित रूप से पैकेज करने के लिए IL-Repack रिपैक का उपयोग करने के लिए अपने .csproj को संपादित कर सकते हैं।

  1. इंस्टॉल Install-Package ILRepack.MSBuild.Task साथ nuget ILRepack.MSBuild.Task पैकेज Install-Package ILRepack.MSBuild.Task
  2. अपने .csproj के AfterBuild अनुभाग को संपादित करें

यहां एक साधारण नमूना है जो आपके प्रोजेक्ट आउटपुट में exampleAssemblyToMerge.dll विलय करता है।

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

न तो आईएलएमर्ज दृष्टिकोण और न ही लार्स होल्म जेन्सेन ने असेंबली रिसेल्व कार्यक्रम को संभालने के लिए प्लगइन होस्ट के लिए काम किया होगा। निष्पादन योग्य एच लोड असेंबली पी गतिशील रूप से कहें और एक अलग असेंबली में परिभाषित इंटरफ़ेस आईपी ​​के माध्यम से इसे एक्सेस करता है। एच में आईपी एम्बेड करने के लिए लार्स के कोड में थोड़ा बदलाव की आवश्यकता होगी:

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

एक ही असेंबली को हल करने के लिए बार-बार प्रयासों को संभालने की चाल और एक नया उदाहरण बनाने के बजाय मौजूदा को वापस करने की चाल।

संपादित करें: अगर यह .NET के क्रमिकरण को खराब कर देता है, तो सुनिश्चित करें कि आपके द्वारा एम्बेडेड सभी असेंबली के लिए शून्य वापस न करें, जिससे मानक व्यवहार में डिफ़ॉल्ट हो। आप इन पुस्तकालयों की एक सूची प्राप्त कर सकते हैं:

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

और अगर पास असेंबली IncludedAssemblies एस्सेम्ब्ली से संबंधित नहीं है तो बस शून्य करें।


मैं आपको .NETZ उपयोगिता की जांच करने की सलाह दूंगा, जो आपकी पसंद की योजना के साथ असेंबली को भी संपीड़ित करता है:

http://madebits.com/netz/help.php#single


मैं एक .vbs स्क्रिप्ट से बुलाया csc.exe संकलक का उपयोग करें।

अपनी xyz.cs स्क्रिप्ट में, निर्देशों के बाद निम्न पंक्तियां जोड़ें (मेरा उदाहरण रेन्सी एसएसएच के लिए है):

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"

सीएससी कमांड बनाने के लिए नीचे दिए गए .vbs स्क्रिप्ट द्वारा रेफरी, रेस और इको टैग उठाए जाएंगे।

फिर मुख्य में असेंबली रिज़ॉल्वर कॉलर जोड़ें:

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

... और कक्षा में कहीं भी रिज़ॉल्वर जोड़ें:

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

    }

मैं .cs फ़ाइल नाम से मिलान करने के लिए vbs स्क्रिप्ट का नाम देता हूं (उदाहरण के लिए ssh.vbs ssh.cs के लिए दिखता है); यह कई बार स्क्रिप्ट को बहुत आसान बनाता है, लेकिन यदि आप मेरे जैसे मूर्ख नहीं हैं तो एक सामान्य लिपि ड्रैग-एंड-ड्रॉप से ​​लक्ष्य .cs फ़ाइल को चुन सकती है:

    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)

यदि वे वास्तव में असेंबली प्रबंधित हैं, तो आप ILMerge उपयोग कर सकते हैं। देशी डीएलएल के लिए, आपके पास थोड़ा और काम होगा।

यह भी देखें: सी ++ विंडोज़ डीएल को सी # एप्लिकेशन एक्सई में कैसे विलय किया जा सकता है?


यह सरल हो सकता है, लेकिन WinRar फ़ाइलों को एक स्वयं निकालने वाले निष्पादन योग्य को संपीड़ित करने का विकल्प देता है।
इसमें बहुत से कॉन्फ़िगर करने योग्य विकल्प हैं: अंतिम आइकन, दिए गए पथ में फ़ाइलों को निकालें, निष्कर्षण के बाद निष्पादित करने के लिए फ़ाइल, निष्कर्षण के दौरान दिखाए गए पॉपअप के लिए कस्टम लोगो / ग्रंथ, कोई पॉपअप विंडो बिल्कुल नहीं, लाइसेंस अनुबंध टेक्स्ट इत्यादि।
कुछ मामलों में उपयोगी हो सकता है।


सी # में हाइब्रिड मूल / प्रबंधित असेंबली बनाने के लिए यह संभव है लेकिन इतना आसान नहीं है। क्या आप सी ++ का उपयोग कर रहे थे, इसके बजाय यह बहुत आसान होगा, क्योंकि विज़ुअल सी ++ कंपाइलर हाइब्रिड असेंबली को जितना आसानी से बना सकता है।

जब तक आपको हाइब्रिड असेंबली का उत्पादन करने की सख्त आवश्यकता न हो, मैं MusiGenesis से सहमत हूं कि यह वास्तव में सी # के साथ होने वाली परेशानी के लायक नहीं है। यदि आपको ऐसा करने की ज़रूरत है, तो शायद इसके बजाय सी ++ / सीएलआई पर जाने के लिए देखें।


हां, पुस्तकालयों के साथ .NET निष्पादन योग्यों को मर्ज करना संभव है। काम पूरा करने के लिए कई टूल उपलब्ध हैं:

  • ILMerge एक उपयोगिता है जिसका उपयोग एकाधिक .NET असेंबली को एक असेंबली में मर्ज करने के लिए किया जा सकता है।
  • मोनो mkbundle , एक exe संकुल और libemono के साथ सभी असेंबली एक बाइनरी पैकेज में पैकेज।
  • IL-Repack रिपैक कुछ अतिरिक्त सुविधाओं के साथ आईएलएमर्ज के लिए एक फ्लॉस परिवर्तनशील है।

इसके अलावा इसे मोनो लिंकर के साथ जोड़ा जा सकता है, जो अप्रयुक्त कोड को हटा देता है और इसके परिणामस्वरूप असेंबली को छोटा बनाता है।

एक और संभावना है .NETZ का उपयोग .NETZ , जो न केवल एक असेंबली को संपीड़ित करने की इजाजत देता है, बल्कि .NETZ को .NETZ पैक कर सकता है। उपरोक्त उल्लिखित समाधानों में अंतर यह है कि .NETZ उन्हें विलय नहीं करता है, वे अलग असेंबली रहते हैं लेकिन एक पैकेज में पैक होते हैं।

.NETZ एक ओपन सोर्स टूल है जो उन्हें छोटा बनाने के लिए Microsoft .NET Framework निष्पादन योग्य (EXE, DLL) फ़ाइलों को संकुचित और पैक करता है।


BoxedApp चेक करें

यह किसी भी ऐप में एक डीएलएल एम्बेड कर सकते हैं। निश्चित रूप से सी # में भी लिखा है :)

आशा करता हूँ की ये काम करेगा।


ILMerge वही करता है जो आप चाहते हैं।


ILMerge असेंबली को एक एकल असेंबली में जोड़ सकता है बशर्ते असेंबली केवल प्रबंधित कोड हो। आप कमांडलाइन ऐप का उपयोग कर सकते हैं, या exe और प्रोग्रामेटिक रूप से विलय के संदर्भ जोड़ सकते हैं। एक जीयूआई संस्करण के लिए Eazfuscator , और .Netz दोनों जिनमें से मुक्त हैं। भुगतान किए गए ऐप्स में BoxedApp और SmartAssembly

अगर आपको अप्रबंधित कोड के साथ असेंबली मर्ज करना है, तो मैं SmartAssembly सुझाव SmartAssembly । मैंने SmartAssembly साथ कभी भी हिचकी नहीं की लेकिन सभी अन्य लोगों के साथ। यहां, यह आवश्यक निर्भरताओं को संसाधनों के रूप में आपके मुख्य exe में एम्बेड कर सकता है।

यदि आप असेंबली प्रबंधित हैं या मिश्रित मोड में अपने संसाधनों में ResolveHandler एम्बेड करके और फिर ResolveHandler के असेंबली ResolveHandler पर भरोसा करते हैं तो आप इसे मैन्युअल रूप से करने की ज़रूरत नहीं कर सकते हैं। यह सबसे खराब मामला अपनाने, यानी अप्रबंधित कोड के साथ असेंबली द्वारा एक स्टॉप समाधान है।

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

यहां कुंजी बाइट्स को फ़ाइल में लिखना और उसके स्थान से लोड करना है। चिकन और अंडे की समस्या से बचने के लिए, आपको यह सुनिश्चित करना होगा कि आप असेंबली तक पहुंचने से पहले हैंडलर घोषित करें और आप लोडिंग (असेंबली रिज़ॉल्यूइंग) भाग के अंदर असेंबली सदस्यों (या असेंबली से निपटने के लिए कुछ भी तत्काल) तक पहुंच नहीं पाते हैं। यह भी सुनिश्चित करने के लिए GetMyApplicationSpecificPath() कि GetMyApplicationSpecificPath() कोई अस्थायी निर्देशिका नहीं है क्योंकि अस्थायी फ़ाइलों को अन्य प्रोग्रामों द्वारा या स्वयं द्वारा मिटाए जाने का प्रयास किया जा सकता है (यह नहीं कि आपका प्रोग्राम डीएलएल तक पहुंचने पर इसे हटा दिया जाएगा, लेकिन कम से कम यह एक उपद्रव है। ऐपडाटा अच्छा स्थान है)। यह भी ध्यान रखें कि आपको प्रत्येक बार बाइट लिखना है, आप स्थान से लोड नहीं कर सकते हैं 'क्योंकि पहले से ही डीएलएल रहता है।

प्रबंधित डीएलएस के लिए, आपको बाइट्स लिखने की आवश्यकता नहीं है, लेकिन सीधे डीएलएल के स्थान से लोड करें, या केवल बाइट्स पढ़ें और असेंबली को स्मृति से लोड करें। इस तरह या तो:

    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.

यदि असेंबली पूरी तरह से अप्रबंधित है, तो आप इस link या this तरह के डीएलएस को लोड करने के तरीके के रूप में देख सकते हैं।





linker