การฝัง DLLs ในการปฏิบัติการที่คอมไพล์แล้ว


618

เป็นไปได้หรือไม่ที่จะฝัง DLL ที่มีอยู่แล้วลงในไฟล์ประมวลผล C # ที่คอมไพล์แล้ว (เพื่อให้คุณมีไฟล์เดียวที่จะเผยแพร่) ถ้าเป็นไปได้คนจะทำยังไงดีล่ะ?

โดยปกติแล้วฉันเจ๋งเพียงแค่ออกจาก DLL ภายนอกและให้โปรแกรมติดตั้งจัดการทุกอย่าง แต่มีคนทำงานอยู่สองคนที่ถามฉันและฉันก็ไม่รู้เหมือนกัน


ฉันอยากจะแนะนำให้คุณตรวจสอบยูทิลิตี้. NETZ ซึ่งจะบีบอัดแอสเซมบลีด้วยรูปแบบที่คุณเลือก: http://madebits.com/netz/help.php#single
Nathan Baulch

2
เป็นไปได้ แต่คุณจะจบลงด้วยการปฏิบัติการขนาดใหญ่ (Base64 จะใช้ในการเข้ารหัส dll ของคุณ)
Paweł Dyda

นอกจากILMergeถ้าคุณไม่ต้องการยุ่งกับสวิตช์บรรทัดคำสั่งฉันแนะนำILMerge-Guiจริงๆ มันเป็นโครงการโอเพ่นซอร์สดีจริงๆ!
tyron

2
@ PawełDyda: คุณสามารถฝังข้อมูลไบนารีแบบดิบลงในภาพ PE (ดูRCDATA ) ไม่มีการแปลงที่ต้องการ (หรือแนะนำ)
IIspectable

คำตอบ:


761

ฉันขอแนะนำให้ใช้Costura.Fody - โดยวิธีที่ดีที่สุดและง่ายที่สุดในการฝังทรัพยากรในชุดประกอบของคุณ มันเป็นแพ็คเกจ NuGet

Install-Package Costura.Fody

หลังจากเพิ่มไปยังโครงการแล้วจะฝังการอ้างอิงทั้งหมดที่คัดลอกไปยังไดเรกทอรีเอาท์พุทโดยอัตโนมัติในแอสเซมบลีหลักของคุณ คุณอาจต้องการล้างไฟล์ฝังตัวด้วยการเพิ่มเป้าหมายในโครงการของคุณ:

Install-CleanReferencesTarget

นอกจากนี้คุณยังสามารถระบุได้ว่าจะรวม pdb ของแยกแอสเซมบลีบางอย่างหรือแยกแอสเซมบลีทันที เท่าที่ฉันรู้มีการสนับสนุนชุดประกอบที่ไม่มีการจัดการด้วย

ปรับปรุง

ขณะที่บางคนกำลังพยายามที่จะเพิ่มการสนับสนุนสำหรับ DNX

อัปเดต 2

สำหรับเวอร์ชั่น Fody ล่าสุดคุณจะต้องมี MSBuild 16 (ดังนั้น Visual Studio 2019) Fody เวอร์ชัน 4.2.1 จะทำ MSBuild 15. (การอ้างอิง: Fody รองรับเฉพาะ MSBuild 16 ขึ้นไปรุ่นปัจจุบัน: 15 )


79
ขอบคุณสำหรับคำแนะนำที่ยอดเยี่ยมนี้ ติดตั้งแพคเกจและคุณทำเสร็จแล้ว มันยังบีบอัดชุดประกอบตามค่าเริ่มต้น
Daniel

9
เกลียดที่จะเป็น 'ฉันด้วย' แต่ฉันก็เช่นกัน - สิ่งนี้ช่วยให้ฉันปวดหัวมาก! ขอบคุณสำหรับคำแนะนำ! สิ่งนี้ทำให้ฉันสามารถจัดแพคเกจทุกอย่างที่ฉันต้องแจกจ่ายเป็น exe เดียวและตอนนี้มันเล็กกว่า exe และ dll เดิมรวมกัน ... ฉันเพิ่งใช้มันมาสองสามวันแล้วดังนั้นฉันจึงไม่สามารถพูดได้ว่าฉัน ' ฉันใส่มันเข้าไปในจังหวะของมัน แต่ไม่ว่าอะไรจะโผล่ขึ้นมาฉันจะเห็นว่านี่เป็นเครื่องมือปกติในกล่องเครื่องมือของฉัน มันใช้งานได้!
mattezell

19
มันเย็น. แต่มีข้อเสียคือ: แอสเซมบลีที่สร้างขึ้นบน Windows จะไม่เข้ากันได้กับไบนารีแบบโมโนโมโน นั่นหมายความว่าคุณไม่สามารถปรับใช้การชุมนุมบน Linux โมโนโดยตรง
Tyler Long

7
น่ารักจัง! หากคุณใช้ vs2018 อย่าลืมไฟล์ FodyWeavers.xml ที่อยู่ที่รูทของโครงการของคุณ
อลันลึก

4
เป็นส่วนเสริมสำหรับความคิดเห็นล่าสุด: เพิ่ม FodyWeavers.xml ด้วยเนื้อหาต่อไปนี้ในโครงการของคุณ: <? xml version = "1.0" encoding = "utf-8"?> <Weavers VerifyAssembly = "true"> <Costura /> </Weavers>
HHenn

88

เพียงคลิกขวาที่โครงการของคุณใน Visual Studio เลือกคุณสมบัติโครงการ -> ทรัพยากร -> เพิ่มทรัพยากร -> เพิ่มไฟล์ที่มีอยู่ ... และใส่รหัสด้านล่างลงใน App.xaml.cs หรือเทียบเท่า

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

นี่คือโพสต์บล็อกเดิมของฉัน: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/


6
คุณสามารถนำพฤติกรรมนี้ออกจากกล่องได้ ลองดูคำตอบของฉันstackoverflow.com/a/20306095/568266
Matthias

4
สิ่งสำคัญเช่นกันในการบันทึกความคิดเห็นที่มีประโยชน์อย่างไม่น่าเชื่อในบล็อกของคุณจาก AshRowe: หากคุณติดตั้งธีมที่กำหนดเองไว้มันจะพยายามแก้ไข PresentationFramework ชุดประกอบที่มีปัญหาและเบิร์น! ตามข้อเสนอแนะของ AshRowe คุณสามารถตรวจสอบว่า dllName มี PresentationFramework อย่างนั้นหรือไม่: ถ้า (dllName.ToLower (). มี ("presentationframework")) คืนค่า null;
YasharBahman

4
สองความคิดเห็นเกี่ยวกับเรื่องนี้ ที่หนึ่ง: คุณควรตรวจสอบว่าbytesมีค่าเป็นโมฆะหรือไม่ถ้าใช่ให้คืนค่าเป็นศูนย์ เป็นไปได้ว่า dll ไม่ได้อยู่ในทรัพยากรหลังจากทั้งหมด สอง: วิธีนี้ใช้ได้เฉพาะในกรณีที่คลาสนั้นไม่มี "การใช้" สำหรับสิ่งใด ๆ จากชุดประกอบนั้น สำหรับเครื่องมือบรรทัดคำสั่งฉันต้องย้ายรหัสโปรแกรมจริงของฉันไปยังไฟล์ใหม่และสร้างโปรแกรมหลักใหม่ขนาดเล็กที่เพิ่งทำสิ่งนี้แล้วเรียกหลักเดิมในคลาสเก่า
Nyerguds

2
ข้อดีของวิธีนี้คือไม่ต้องพึ่งพาการติดตั้ง libs ภายนอกเพื่อให้ได้ฟังก์ชันการทำงานที่ต้องการ ข้อเสียของวิธีนี้คือมันมีประโยชน์เฉพาะเมื่อมันมาถึงการจัดการ DLLs - interop dlls (อย่างน้อยที่สุดเท่าที่การทดสอบของฉันไป) ไม่ยิงเหตุการณ์แอสเซมบลีแก้ปัญหาและแม้ว่าพวกเขาจะ Assembly.Load (<bytes .dll>) ไม่บรรลุผลตามที่ต้องการบนถนน stackoverflow.com/questions/13113131/…เพียง 2c ของฉันในเรื่อง
XDS

3
ในกรณีที่ใครก็ตามพบปัญหาของฉัน: หาก.dllชื่อนั้นประกอบด้วยเครื่องหมายขีดคั่น (เช่นtwenty-two.dll) ชื่อนั้นจะถูกแทนที่ด้วยเครื่องหมายขีดล่าง (เช่นtwenty_two.dll) คุณสามารถเปลี่ยนรหัสบรรทัดนี้เป็น:dllName = dllName.Replace(".", "_").Replace("-", "_");
Micah Vertal

87

หากพวกเขากำลังมีการจัดการจริงประกอบคุณสามารถใช้ILMerge สำหรับ DLLs ดั้งเดิมคุณจะต้องทำงานอีกเล็กน้อย

ดูเพิ่มเติมที่: C + + windows dll จะถูกรวมเข้ากับ exe ได้อย่างไร


ฉันสนใจการผสาน Native Native DLL มีวัสดุใด ๆ หรือไม่
Baiyan Huang


@BaiyanHuang ดูที่github.com/boxedapp/bxilmergeความคิดนี้คือการทำให้ "ILMerge" เป็นที่อยู่ของคนพื้นเมือง
Artem Razin

นักพัฒนา VB NET อย่างฉันไม่ต้องกลัวC++ที่ลิงค์ ILMerge ยังทำงานได้ง่ายมากสำหรับ VB NET ดูที่นี่https://github.com/dotnet/ILMerge Thanks @ Shog9
Ivan Ferrer Villa

26

ใช่เป็นไปได้ที่จะรวม. NET executables เข้ากับไลบรารี มีเครื่องมือหลายอย่างที่พร้อมใช้งานให้เสร็จ:

  • ILMergeเป็นโปรแกรมอรรถประโยชน์ที่สามารถใช้เพื่อรวมแอสเซมบลี. NET หลาย ๆ แอสเซมบลีเดียว
  • คลิกที่นี่ , แพคเกจ exe และแอสเซมบลีที่มี libmono ทั้งหมดลงในแพคเกจไบนารีเดียว
  • IL-Repackเป็น FLOSS เปลี่ยนแปลงได้กับ ILMerge พร้อมกับคุณสมบัติเพิ่มเติมบางอย่าง

นอกจากนี้ยังสามารถใช้ร่วมกับMono Linkerซึ่งลบโค้ดที่ไม่ได้ใช้และทำให้แอสเซมบลีที่เกิดขึ้นมีขนาดเล็กลง

ความเป็นไปได้อีกอย่างหนึ่งคือการใช้. NETZซึ่งไม่เพียง แต่อนุญาตให้มีการบีบอัดแอสเซมบลีเท่านั้น แต่ยังสามารถแพ็ค DLLs ลงใน exe ได้โดยตรง ความแตกต่างของโซลูชันที่กล่าวถึงข้างต้นคือ. NETZ ไม่ได้รวมเข้าด้วยกันพวกเขายังคงมีแอสเซมบลีแยกต่างหาก แต่จะรวมอยู่ในแพ็คเกจเดียว

.NETZ เป็นเครื่องมือโอเพนซอร์ซที่บีบอัดและจัดเก็บไฟล์ที่เรียกใช้งานได้ของ Microsoft .NET Framework (EXE, DLL) เพื่อให้มีขนาดเล็กลง


ดูเหมือนว่า NETZ จะหายไป
Rbjz

ว้าว - ฉันคิดว่าในที่สุดฉันก็พบมันแล้วฉันก็อ่านความคิดเห็นนี้ ดูเหมือนว่าจะหายไปโดยสิ้นเชิง มีส้อมหรือไม่?
Mafii

มันเพิ่งย้ายไปที่ GitHub และไม่มีการเชื่อมโยงกับเว็บไซต์อีกต่อไป ... ดังนั้น "ไปโดยสิ้นเชิง" จึงเป็นการกล่าวเกินจริง เป็นไปได้ว่าส่วนใหญ่จะไม่ได้รับการสนับสนุนอีกต่อไป แต่ยังคงอยู่ที่นั่น ฉันอัพเดทลิงค์
Bobby

20

ILMergeสามารถรวมแอสเซมบลีเข้ากับแอสเซมบลีเดียวได้โดยที่แอสเซมบลีมีรหัสที่ได้รับการจัดการเท่านั้น คุณสามารถใช้แอพ commandline หรือเพิ่มการอ้างอิงไปที่ exe และการเขียนโปรแกรมแบบผสาน สำหรับรุ่น GUI จะมีEazfuscatorและ. Netzทั้งสองอย่างนั้นฟรี ปพลิเคชันที่เรียกชำระแล้วรวมBoxedAppและsmartassembly

หากคุณมีการประกอบการผสานด้วยรหัสที่ไม่มีการจัดการผมจะแนะนำsmartassembly ฉันไม่เคย hiccups กับSmartAssemblyแต่กับคนอื่น ๆ ทั้งหมด ที่นี่สามารถฝังการอ้างอิงที่ต้องการเป็นทรัพยากรของ exe หลักของคุณ

คุณสามารถทำทั้งหมดนี้ด้วยตนเองไม่ต้องกังวลถ้าการชุมนุมมีการจัดการหรือในโหมดผสมโดยการฝัง dll 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()ไม่ใช่ไดเรกทอรีชั่วคราวเนื่องจากไฟล์ temp อาจพยายามลบโดยโปรแกรมอื่น ๆ หรือด้วยตัวเอง (ไม่ใช่ว่ามันจะถูกลบในขณะที่โปรแกรมของคุณกำลังเข้าถึง dll แต่อย่างน้อยก็น่ารำคาญ AppData ดี สถานที่) นอกจากนี้โปรดทราบว่าคุณต้องเขียนไบต์ทุกครั้งคุณไม่สามารถโหลดได้จากตำแหน่งเพียงเพราะ cos dll นั้นมีอยู่แล้ว

สำหรับ DLLs ที่ได้รับการจัดการคุณไม่จำเป็นต้องเขียนไบต์ แต่โหลดโดยตรงจากตำแหน่งของ dll หรือเพียงแค่อ่านไบต์และโหลดแอสเซมบลีจากหน่วยความจำ ชอบสิ่งนี้หรือมากกว่า:

    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.

หากแอสเซมบลีไม่ได้รับการจัดการอย่างสมบูรณ์คุณสามารถดูลิงค์นี้หรือวิธีการโหลด dll ดังกล่าว


โปรดทราบว่าจำเป็นต้องตั้งค่า "Build Action" ของทรัพยากรเป็น "Embedded Resource"
Mavamaarten

@Mavamaarten ไม่จำเป็น หากมีการเพิ่มลงใน Resources.resx ของโครงการล่วงหน้าคุณไม่จำเป็นต้องทำเช่นนั้น
Nyerguds

2
EAZfuscator ตอนนี้เป็นเชิงพาณิชย์
Telemat

16

ส่วนที่ตัดตอนมาจากเจฟฟรีย์ริกเตอร์นั้นดีมาก ในระยะสั้นเพิ่มห้องสมุดเป็นทรัพยากรที่ฝังตัวและเพิ่มการเรียกกลับก่อนสิ่งอื่นใด นี่คือรุ่นของรหัส (พบได้ในความคิดเห็นของหน้าของเขา) ที่ฉันใส่ไว้ที่จุดเริ่มต้นของวิธีการหลักสำหรับแอปคอนโซล

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
เปลี่ยนไปเล็กน้อยทำงานได้เลยเพื่อน!
Sean Ed-Man

โครงการlibz.codeplex.comใช้กระบวนการนี้ แต่จะทำสิ่งอื่น ๆ เช่นจัดการตัวจัดการเหตุการณ์สำหรับคุณและรหัสพิเศษบางอย่างที่จะไม่ทำลาย " แคตตาล็อกกรอบการจัดการขยายความ " (ซึ่งโดยตัวมันเองกระบวนการนี้จะทำลาย)
Scott Chamberlain

เยี่ยมมาก !! ขอบคุณ @Steve
Ahmer Afzal

14

หากต้องการขยายบน@ Bobby's asnwerด้านบน คุณสามารถแก้ไข. csproj ของคุณเพื่อใช้IL-Repackเพื่อจัดเก็บไฟล์ทั้งหมดลงในแอสเซมบลีเดียวโดยอัตโนมัติเมื่อคุณสร้าง

  1. ติดตั้ง nuget ILRepack.MSBuild.Task แพ็คเกจด้วย Install-Package ILRepack.MSBuild.Task
  2. แก้ไขส่วน AfterBuild ของ. csproj ของคุณ

นี่คือตัวอย่างง่ายๆที่ผสาน 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>

1
ไวยากรณ์สำหรับ IL-Repack มีการเปลี่ยนแปลงตรวจสอบ README.md ที่อยู่บน github repo ที่เชื่อมโยง ( github.com/peters/ILRepack.MSBuild.Task ) วิธีนี้เป็นวิธีเดียวที่ใช้ได้ผลสำหรับฉันและฉันสามารถใช้ wildcard เพื่อจับคู่ dll ทั้งหมดที่ฉันต้องการรวมไว้
Seabass77

8

คุณสามารถเพิ่ม DLLs เป็นทรัพยากรที่ฝังตัวจากนั้นให้โปรแกรมของคุณแตกไฟล์เหล่านั้นลงในไดเรกทอรีแอปพลิเคชันเมื่อเริ่มต้น (หลังจากตรวจสอบเพื่อดูว่ามีอยู่แล้ว)

แม้ว่าไฟล์ติดตั้งนั้นง่ายต่อการทำ แต่ฉันไม่คิดว่ามันจะคุ้มค่า

แก้ไข: เทคนิคนี้จะง่ายกับ. NET ประกอบ ด้วย non-.NET DLLs มันจะทำงานได้มากขึ้น (คุณต้องหาที่ที่จะแกะไฟล์และลงทะเบียนพวกมันเป็นต้น)


ที่นี่คุณมีบทความที่ยอดเยี่ยมที่อธิบายวิธีการทำเช่นนี้: codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource
สีน้ำเงิน

8

ผลิตภัณฑ์ที่สามารถจัดการกับปัญหานี้อย่างหรูหราก็คือ smartassembly ที่SmartAssembly.com ผลิตภัณฑ์นี้จะนอกเหนือไปจากการรวมการพึ่งพาทั้งหมดใน DLL เดียว (ทางเลือก) ทำให้รหัสของคุณงงงวยลบ meta-data พิเศษเพื่อลดขนาดไฟล์ผลลัพธ์และยังสามารถเพิ่มประสิทธิภาพ IL จริงเพื่อเพิ่มประสิทธิภาพรันไทม์

นอกจากนี้ยังมีคุณสมบัติการจัดการ / การรายงานข้อยกเว้นระดับโลกที่เพิ่มลงในซอฟต์แวร์ของคุณ (หากต้องการ) ที่อาจมีประโยชน์ ฉันเชื่อว่ามันมี API บรรทัดคำสั่งเพื่อให้คุณสามารถทำให้เป็นส่วนหนึ่งของกระบวนการสร้างของคุณ


7

ทั้งวิธี ILMerge และ Lars Holm Jensen การจัดการเหตุการณ์ AssemblyResolve จะไม่ทำงานสำหรับโฮสต์ปลั๊กอิน พูดได้ว่าโหลดHประกอบได้Pแบบไดนามิกและเข้าถึงผ่านอินเตอร์เฟสIP ที่กำหนดไว้ในชุดประกอบแยกต่างหาก หากต้องการฝังIPลงในHหนึ่งจะต้องมีการแก้ไขโค้ดของ 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;
};  

เคล็ดลับในการจัดการความพยายามซ้ำ ๆ เพื่อแก้ไขชุดประกอบเดิมและส่งคืนชุดเดิมที่มีอยู่แทนที่จะสร้างอินสแตนซ์ใหม่

แก้ไข: เพื่อไม่ให้เสียการทำให้เป็นอนุกรมของ. NET ตรวจสอบให้แน่ใจว่าได้คืนค่า null สำหรับชุดประกอบทั้งหมดที่ไม่ได้ฝังอยู่ในชุดของคุณดังนั้นจึงเริ่มต้นการทำงานมาตรฐาน คุณสามารถรับรายชื่อของห้องสมุดเหล่านี้ได้โดย:

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

และเพิ่งกลับ null IncludedAssembliesถ้าผ่านการชุมนุมไม่ได้อยู่ใน


ขออภัยที่โพสต์เป็นคำตอบแทนที่จะเป็นความคิดเห็น ฉันไม่มีสิทธิ์แสดงความคิดเห็นคำตอบของผู้อื่น
Ant_222

5

.NET Core 3.0 สนับสนุนการคอมไพล์เป็น. exe

คุณลักษณะนี้เปิดใช้งานโดยการใช้คุณสมบัติต่อไปนี้ในไฟล์โครงการของคุณ (.csproj):

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

ทำได้โดยไม่ต้องใช้เครื่องมือภายนอก

ดูคำตอบของฉันสำหรับคำถามนี้สำหรับรายละเอียดเพิ่มเติม


3

มันอาจฟังดูง่าย แต่ WinRar ให้ตัวเลือกในการบีบอัดไฟล์หลาย ๆ ไฟล์เพื่อให้สามารถเรียกทำงานได้แบบแยกไฟล์เอง
แต่ก็มีจำนวนมากตัวเลือกการกำหนดค่า: ไอคอนสุดท้ายดึงไฟล์ไปยังเส้นทางที่กำหนดไฟล์ที่จะดำเนินการหลังจากการสกัดโลโก้ที่กำหนดเอง / ตำราสำหรับป๊อปอัพที่แสดงในระหว่างการสกัดไม่มีหน้าต่างป๊อปอัพที่ทุกข้อความข้อตกลงใบอนุญาตอื่น ๆ
อาจจะมีประโยชน์ในบางกรณี .


Windows เองมีเครื่องมือคล้ายกันที่เรียกว่า iexpress นี่คือบทแนะนำ
Ivan Ferrer Villa

2

ฉันใช้คอมไพเลอร์ csc.exe ที่เรียกจากสคริปต์. vbs

ในสคริปต์ xyz.cs ของคุณเพิ่มบรรทัดต่อไปนี้หลังจากคำสั่ง (ตัวอย่างของฉันสำหรับ 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"

แท็ก ref, res และ ico จะถูกหยิบขึ้นมาโดยสคริปต์. vbs ด้านล่างเพื่อจัดทำคำสั่ง csc

จากนั้นเพิ่มตัวเรียกเครื่องมือแก้ไขแอสเซมบลีในหน้าหลัก:

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

... และเพิ่มตัวแก้ไขที่อื่นในคลาส:

    แอสเซมบลีปัจจุบัน CurrentDomain_AssemblyResolve (ผู้ส่งวัตถุ, ResolveEventArgs args)
    {
        สตริง resourceName = ใหม่ AssemblyName (args.Name) .Name + ".dll";

        using (var stream = Assembly.GetExecutingAssembly (). GetManifestResourceStream (resourceName))
        {
            Byte [] assemblyData = new Byte [stream.Length];
            กระแสข้อมูลอ่าน (assemblyData, 0, assemblyData.Length);
            ผลตอบแทนการชุมนุมโหลด (assemblyData);
        }

    }

ฉันตั้งชื่อสคริปต์ vbs ให้ตรงกับชื่อไฟล์. cs (เช่น ssh.vbs ค้นหา ssh.cs); สิ่งนี้ทำให้การเรียกใช้สคริปต์ง่ายขึ้นหลายครั้งมาก แต่ถ้าคุณไม่ใช่คนงี่เง่าอย่างฉันสคริปต์ทั่วไปสามารถรับไฟล์. cs เป้าหมายจากการลากแล้วปล่อย:

    ติ่มชื่อ _, oShell, fso
    ตั้งค่า oShell = CreateObject ("Shell.Application")
    ตั้งค่า fso = CreateObject ("Scripting.fileSystemObject")

    ใช้ชื่อสคริปต์ VBS เป็นชื่อไฟล์เป้าหมาย
    '################################################
    name_ = แยก (wscript.ScriptName, ".") (0)

    'รับ DLL ภายนอกและชื่อไอคอนจากไฟล์. CS
    '################################################# ######
    Const OPEN_FILE_FOR_READING = 1
    ตั้งค่า objInputFile = fso.OpenTextFile (name_ & ".cs", 1)

    อ่านทุกอย่างลงในอาร์เรย์
    '#############################
    inputData = แยก (objInputFile.ReadAll, vbNewline)

    สำหรับแต่ละ strData In inputData

        ถ้า left (strData, 7) = "// + ref>" แล้ว 
            csc_references = csc_references & "/ reference:" & trim (แทนที่ (strData, "// + ref>", "" ")) &" "
        จบถ้า

        ถ้า left (strData, 7) = "// + res>" แล้ว 
            csc_resources = csc_resources & "/ resource:" & trim (แทนที่ (strData, "// + res>", "" ")) &" "
        จบถ้า

        ถ้า left (strData, 7) = "// + ico>" แล้ว 
            csc_icon = "/ win32icon:" & ตัด (แทนที่ (strData, "// + ico>", "")) & ""
        จบถ้า
    ต่อไป

    objInputFile.Close


    'รวบรวมไฟล์
    '################
    oShell.ShellExecute "c: \ windows \ microsoft.net \ framework \ v3.5 \ csc.exe", "/ เตือน: 1 / เป้าหมาย: exe" & csc_references & csc_resources & csc_icon & "" & name_ & ".cs" , "", "runas", 2


    WScript.Quit (0)

0

เป็นไปได้ แต่ไม่ใช่ทั้งหมดที่ง่ายนักในการสร้างชุดประกอบแบบเนทีฟ / จัดการแบบไฮบริดใน C # คุณใช้ C ++ แทนหรือเปล่าเพราะคอมไพเลอร์ Visual C ++ สามารถสร้างแอสเซมบลีไฮบริดได้ง่ายเหมือนอย่างอื่น

เว้นแต่คุณจะมีข้อกำหนดที่เข้มงวดในการผลิตชุดประกอบแบบผสมฉันเห็นด้วยกับ MusiGenesis ว่านี่ไม่คุ้มกับปัญหาที่จะทำกับ C # หากคุณจำเป็นต้องทำบางทีดูที่การย้ายไปที่ C ++ / CLI แทน


0

โดยทั่วไปคุณจะต้องใช้เครื่องมือสร้างโพสต์รูปแบบเพื่อดำเนินการรวมแอสเซมบลีที่คุณอธิบาย มีเครื่องมือฟรีที่เรียกว่า Eazfuscator (eazfuscator.blogspot.com/) ซึ่งออกแบบมาสำหรับการ bytecode mangling ที่จัดการการรวมกันของการชุมนุม คุณสามารถเพิ่มสิ่งนี้ลงในบรรทัดคำสั่งการสร้างโพสต์กับ Visual Studio เพื่อรวมแอสเซมบลีของคุณ แต่ระยะของคุณจะแตกต่างกันเนื่องจากปัญหาที่จะเกิดขึ้นในสถานการณ์การผสานแอสเซมบลี trival

คุณสามารถตรวจสอบเพื่อดูว่าบิลด์สร้างความไม่แน่นอน NANT มีความสามารถในการรวมแอสเซมบลีหลังการสร้างหรือไม่ แต่ฉันไม่คุ้นเคยกับ NANT มากพอที่จะบอกว่าการทำงานนั้นถูกสร้างขึ้นหรือไม่

นอกจากนี้ยังมีปลั๊กอิน Visual Studio จำนวนมากที่จะทำการรวมแอสเซมบลีเป็นส่วนหนึ่งของการสร้างแอปพลิเคชัน

อีกวิธีหนึ่งถ้าคุณไม่ต้องการให้สิ่งนี้ทำโดยอัตโนมัติมีเครื่องมือมากมายเช่น ILMerge ที่จะรวมแอสเซมบลี. net ไว้ในไฟล์เดียว

ปัญหาที่ใหญ่ที่สุดที่ฉันเคยมีในการรวมแอสเซมบลีคือถ้าพวกเขาใช้ namespaces ที่คล้ายกัน หรือแย่กว่านั้นคืออ้างอิงรุ่นที่แตกต่างของ dll เดียวกัน (ปัญหาของฉันคือโดยทั่วไปกับไฟล์ NUnit dll)


1
Eazfuscator จะโทรหา IlMerge, AFAIK
Bobby

+1 บ๊อบบี้ ฉันควรจะจำได้ว่า เกี่ยวกับ Eazfucator ทั้งหมดที่คุณคิดว่าเป็นนามธรรมการโทรจริงไปยัง ILMerge ด้วยไฟล์ปรับแต่งทั่วไป
wllmsaccnt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.