Встраивание DLL в скомпилированный исполняемый файл

Можно ли встроить уже существующую DLL в скомпилированный исполняемый файл C # (чтобы у вас был только один файл для распространения)? Если возможно, как бы это сделать?

Обычно мне нравится оставлять библиотеки DLL снаружи, а программа установки все обрабатывает, но на работе было несколько человек, которые спрашивали меня об этом, а я, честно говоря, не знаю.


person Merus    schedule 09.10.2008    source источник
comment
Я бы порекомендовал вам попробовать утилиту .NETZ, которая также сжимает сборку по выбранной вами схеме: http://madebits.com/netz/help.php#single   -  person Nathan Baulch    schedule 10.10.2008
comment
Это возможно, но вы получите большой исполняемый файл (Base64 будет использоваться для кодирования вашей dll).   -  person Paweł Dyda    schedule 28.10.2010
comment
Помимо ILMerge, если вы не хотите возиться с переключателями командной строки, я действительно рекомендую ILMerge-Gui. Это действительно хороший проект с открытым исходным кодом!   -  person tyron    schedule 19.12.2012
comment
@ PawełDyda: вы можете встроить необработанные двоичные данные в образ PE (см. RCDATA). Никаких преобразований не требуется (или рекомендуется).   -  person IInspectable    schedule 26.02.2017


Ответы (16)


Я настоятельно рекомендую использовать 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)

person Matthias    schedule 30.11.2013
comment
Спасибо за это замечательное предложение. Установите пакет, и все готово. По умолчанию он даже сжимает сборки. - person Daniel; 19.12.2013
comment
Ненавижу быть «я тоже», но и я тоже - это избавило меня от головной боли! Спасибо за рекомендацию! Это позволило мне упаковать все, что мне нужно для распространения, в один exe, и теперь он меньше, чем исходный exe и библиотеки, которые были объединены ... Я использую это всего несколько дней, поэтому я не могу сказать, что я ' Я прошел через это испытание, но если не произойдет ничего плохого, я вижу, что это стало обычным инструментом в моем наборе инструментов. Просто работает! - person mattezell; 12.06.2014
comment
Это круто. Но есть недостаток: сборка, созданная в Windows, больше не двоично совместима с моно Linux. Это означает, что вы не можете развернуть сборку в Linux mono напрямую. - person Tyler Long; 17.06.2014
comment
У Costura, похоже, нет крючков для автоматического обновления через Интернет. См. Мой вопрос: stackoverflow.com / questions / 28323709 / - person Pompair; 04.02.2015
comment
Создание чистого выходного каталога Существует также командлет PowerShell для автоматической установки этой цели в ваш проект. В консоли диспетчера пакетов введите: PM ›Install-CleanReferencesTarget - person Siva; 23.07.2015
comment
Он отлично работал с Visual Studio 2013, но я больше не работаю с VS 2015 (Enterprise) - у кого-нибудь такая же проблема!?!? - person CeOnSql; 28.07.2015
comment
@CeOnSql У меня такая же проблема в Windows 8.1 и 10 с сообществом VS 2015. Если у кого-то есть решение, дайте мне знать! - person alexyorke; 03.08.2015
comment
@ alexy13: Поскольку у меня такая большая репутация благодаря этому посту, я чувствую ответственность за его обновление. К сожалению, сейчас я не могу это проверить. Буду очень признателен, если один из вас создаст здесь проблему github.com/Fody/Costura/issues. - person Matthias; 03.08.2015
comment
@Siva: Спасибо за подсказку. Добавил в пост. - person Matthias; 06.08.2015
comment
Могу ли я включить текстовый файл в exe ?? У меня тоже есть родная библиотека dll и txt. Могу ли я сделать с помощью Costura.Fody только один exe-файл ?? - person Md. Yusuf; 08.02.2016
comment
@Ciprian на самом деле я не использовал костуру. - person Md. Yusuf; 10.10.2016
comment
Кто-нибудь хотел бы предоставить образец того, как вы сливаете это запутанным образом? Я имею в виду, что .exe запутывается перед слиянием. - person newbieguy; 01.04.2017
comment
Просто попробовал с VS 2015 R3 и сработал. Уменьшение общего размера файла для загрузки на 40%. Жаль, что имя NuGet так сложно запомнить (никогда бы не нашел его, если бы не этот ответ). - person Gone Coding; 26.04.2017
comment
Просто голова вверх .. У меня были серьезные проблемы с установкой Costura.Fody с использованием всех обновлений VS 2013. Очевидно, последняя сборка 1.4.1 не работает с 2013 годом. Я успешно установил 1.4 из консоли nuget, но у меня были ошибки компилятора. Я закончил тем, что вручную добавил их в качестве ресурсов проекта, поскольку подход Fody в конце концов потратил мое время. Я могу обновить его, как только закончу свой проект, потому что простота и сжатие звучат УДИВИТЕЛЬНО - person waltmagic; 18.05.2017
comment
Кстати ... У меня был проект с использованием Fody в Visual Studio 2012. Все работало КРАСИВО! Теперь я обновился до Visual Studio 2017, и Fody полностью разочаровался; Совсем не работает. - person eidylon; 25.05.2017
comment
@CeOnSql и alexy13 отлично работают в VS2015 update 3, 14.0.254431.01 - person Mehdi Dehghani; 11.09.2017
comment
Спасатель. Для людей, которые не знают, как установить Costura.Fody, просто зайдите в VS, в меню Инструменты- ›Диспетчер пакетов NuGet-› Консоль диспетчера пакетов. Теперь вы переходите к команде Install-Package Costura.Fody. Перестройте приложение, и все будет хорошо. Просто как тот! Не будучи здесь высокомерным, мне тоже пришлось разобраться в себе. - person Eldoïr; 22.09.2017
comment
он не встраивает SQLite.Interop.dll из библиотеки System.Data.SQLite.Core - person Gene R; 12.02.2018
comment
Это прекрасно! Если вы используете vs2018, не забудьте, что файл FodyWeavers.xml должен находиться в корне вашего проекта. - person Alan Deep; 14.04.2018
comment
Это действительно прекрасно и в 2018 году! :) Очень полезно! - person Rigerta; 07.06.2018
comment
В качестве дополнения к последнему комментарию: добавьте в свой проект файл FodyWeavers.xml следующего содержания: ‹? Xml version = 1.0 encoding = utf-8?› ‹Weavers VerifyAssembly = true› ‹Costura /› ‹/Weavers› - person HHenn; 29.08.2018
comment
Все еще работает в 2018 году, но вам нужно следовать руководству из readme.md здесь github.com/Fody/Costura - person super sahar; 27.11.2018
comment
это работа со ссылками, добавленными из NUGETPackages. Но не работает, если я добавлю решение существующего проекта в качестве ссылки на него. Кто-нибудь может помочь, почему это? - person nsds; 07.02.2019
comment
Для тех, кто получает сообщение об ошибке с MSBuild: stackoverflow.com/a/55843973/6674014 - person DCOPTimDowd; 06.05.2019
comment
Для меня это не решение - Symantec пометила этот пакет Nuget как вирус и автоматически удалила его. - person DAG; 10.01.2020
comment
Команда консоли диспетчера пакетов Install-CleanReferencesTarget больше недействительна и завершится ошибкой. В текущих версиях это автоматизировано. Также для Visual Studio 2017 (с использованием MSBuild 15) установите Nugets Fody 4.2.1 и Costura.Fody 3.3.3 для успешной компиляции. - person Arvo Bowen; 21.02.2020
comment
Надеюсь, все в порядке ... последние 15 минут он устанавливает дополнительные пакеты ... - person CTZStef; 22.07.2021

Просто щелкните свой проект правой кнопкой мыши в 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/

person Lars Holm Jensen    schedule 15.06.2011
comment
Вы можете настроить такое поведение прямо из коробки. Посмотрите мой ответ stackoverflow.com/a/20306095/568266 - person Matthias; 01.12.2013
comment
Также важно отметить НЕВЕРОЯТНО полезный комментарий к вашему блогу от AshRowe: если у вас установлена ​​настраиваемая тема, она попытается разрешить сборку PresentationFramework.Theme, которая вылетает и горит! Согласно предложению AshRowe, вы можете просто проверить, содержит ли dllName PresentationFramework следующим образом: if (dllName.ToLower (). Contains (presentationframework)) return null; - person YasharBahman; 31.01.2014
comment
Два комментария по этому поводу. Первый: вы должны проверить, является ли bytes нулевым, и если да, вернуть там ноль. Возможно, в конце концов, dll не находится в ресурсах. Два: это работает только в том случае, если сам класс не используется ни для чего из этой сборки. Для инструментов командной строки мне пришлось переместить реальный программный код в новый файл и создать небольшую новую основную программу, которая просто делает это, а затем вызывает исходный основной код в старом классе. - person Nyerguds; 26.05.2014
comment
В связи с этим, это можно использовать для компиляции решения с двумя проектами в один exe-файл ... все, что вам нужно сделать, это сделать так, чтобы сценарии предварительной сборки проекта встраивания перезаписывали dll, используемую в качестве ресурса, со встроенной dll из встроенный проект, и заставьте сценарии после сборки внедренного проекта удалить библиотеки DLL из выходной папки решения. - person Nyerguds; 26.05.2014
comment
Еще одно полезное замечание: если вы используете byte[] bytes = rm.GetObject(dllName) as byte[];, вы не можете получить там ошибки приведения, опять же, если система каким-то образом случайно выбрала существующий ресурс другого типа. - person Nyerguds; 04.06.2014
comment
Где я могу найти App.xaml.cs или подобное в Winforms? Это файл, который мне нужно создать? - person Dan W; 29.06.2015
comment
Итак, ваш ответ предназначен для WPF. У меня получилось работать с Winforms. После добавления ресурса, как вы сказали, просто поместите строку AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve); перед строкой InitializeComponent(); в конструкторе формы. Затем поместите весь System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) метод в любое место. Скомпилируйте и запустите. Вот и все. Это делает ваше решение даже проще, чем ответ с наивысшей оценкой, поскольку нет сторонней утилиты для загрузки. - person Dan W; 29.06.2015
comment
@DanW: Приложения с нормальной формой имеют просто файл program.cs, который создает фактический объект формы и запускает его. Вы можете просто поместить его туда перед созданием объекта формы. В любом случае, там больше ничего быть не должно. Ваше решение может не работать, поскольку фактический класс, содержащий этот код, не может использовать загруженный таким образом материал сборки, поскольку разрешение сборки выполняется до выполнения конструктора. - person Nyerguds; 24.08.2015
comment
Достоинством этого подхода является то, что он не полагается на установку каких-либо внешних библиотек для достижения желаемой функциональности. Обратной стороной этого подхода является то, что он полезен только когда дело доходит до управляемых библиотек DLL - библиотеки взаимодействия (по крайней мере, в рамках моего тестирования) не запускают событие assemblyresolve, и даже если они выполнили Assembly.Load (‹байтов некоторого взаимодействия .dll ›) не дает желаемого эффекта в будущем. stackoverflow .com / questions / 13113131 / Только мой 2с по этому поводу - person XDS; 28.08.2015
comment
На всякий случай, если кто-нибудь столкнется с моей проблемой: если имя .dll содержит дефисы (например, twenty-two.dll), они также будут заменены подчеркиванием (например, twenty_two.dll). Вы можете изменить эту строку кода на это: dllName = dllName.Replace(".", "_").Replace("-", "_"); - person Micah Vertal; 14.07.2016
comment
Когда я это сделаю, мое решение не будет компилироваться. Выдает ошибку везде, где я использовал класс из dll - person Notts90 supports Monica; 03.12.2019
comment
@ Notts90 Вам все равно нужно ссылаться на библиотеку для компиляции решения - person Lars Holm Jensen; 06.12.2019

Если это действительно управляемые сборки, вы можете использовать ILMerge. Для нативных DLL вам придется немного поработать.

См. также: Как можно объединить dll Windows C ++ с исполняемым файлом приложения C #?

person Shog9    schedule 09.10.2008
comment
Меня интересует слияние Native DLL, есть ли материалы? - person baye; 05.03.2009
comment
См. Также: stackoverflow.com/questions/108971/ - person Milan Gardian; 10.04.2012
comment
@BaiyanHuang посмотрите на github.com/boxedapp/bxilmerge, идея состоит в том, чтобы сделать ILMerge для нативных Dll. - person Artem Razin; 17.02.2020
comment
Разработчики VB NET, такие как я, не бойтесь этого C++ по ссылке. ILMerge также очень легко работает с VB NET. См. Здесь https://github.com/dotnet/ILMerge. Спасибо @ Shog9 - person Ivan Ferrer Villa; 25.05.2020

Да, можно объединять исполняемые файлы .NET с библиотеками. Для выполнения этой работы доступно несколько инструментов:

  • ILMerge - это служебная программа, которую можно использовать для объединения нескольких сборок .NET в одну сборку.
  • Mono mkbundle, упаковывает exe и все сборки с libmono в один бинарный пакет.
  • IL-Repack является альтернативой ILMerge FLOSS с некоторыми дополнительными функциями.

Кроме того, это можно комбинировать с Mono Linker, который удаляет неиспользуемый код и, следовательно, делает в результате сборка поменьше.

Другая возможность - использовать .NETZ, который не только позволяет сжимать сборку, но также может упаковать DLL прямо в exe. Отличие от вышеупомянутых решений заключается в том, что .NETZ не объединяет их, они остаются отдельными сборками, а упаковываются в один пакет.

.NETZ - это инструмент с открытым исходным кодом, который сжимает и упаковывает исполняемые файлы Microsoft .NET Framework (EXE, DLL), чтобы уменьшить их размер.

person Bobby    schedule 28.10.2010
comment
NETZ, кажется, ушел - person Rbjz; 10.06.2016
comment
Вау - я думал, что наконец нашел, потом прочитал этот комментарий. Кажется, он полностью ушел. Есть ли вилки? - person Mafii; 08.10.2016
comment
Ну, он только что переместился на GitHub, и на него больше нет ссылок на веб-сайте ... так что полностью исчез - это преувеличение. Скорее всего, он больше не поддерживается, но он все еще есть. Обновил ссылку. - person Bobby; 08.10.2016

ILMerge может объединять сборки в одну единственную сборку при условии сборка имеет только управляемый код. Вы можете использовать приложение командной строки или добавить ссылку на exe и программно слить. Для версии с графическим интерфейсом есть Eazfuscator, а также .Netz, оба из которых бесплатны. Платные приложения включают BoxedApp и SmartAssembly.

Если вам нужно объединить сборки с неуправляемым кодом, я бы предложил SmartAssembly. У меня никогда не было проблем с SmartAssembly, но со всеми остальными. Здесь он может встроить необходимые зависимости в качестве ресурсов в ваш основной исполняемый файл.

Вы можете сделать все это вручную, не беспокоясь о том, управляется ли сборка или в смешанном режиме, путем встраивания dll в свои ресурсы и последующего использования сборки ResolveHandler AppDomain. Это универсальное решение, основанное на худшем случае, то есть сборках с неуправляемым кодом.

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() не является временным каталогом, поскольку временные файлы могут быть удалены другими программами или вами (не то, чтобы они были удалены, пока ваша программа обращается к dll, но, по крайней мере, это неприятно. AppData хорошее расположение). Также обратите внимание, что вам нужно записывать байты каждый раз, вы не можете загружать из местоположения только потому, что dll уже находится там.

Для управляемых dll вам нужно не записывать байты, а загружать напрямую из местоположения 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.

person nawfal    schedule 15.05.2012
comment
Обратите внимание, что действие сборки ресурса должно быть установлено на Embedded Resource. - person Mavamaarten; 18.01.2014
comment
@Mavamaarten Не обязательно. Если он заранее добавлен в Resources.resx проекта, в этом нет необходимости. - person Nyerguds; 26.05.2014
comment
EAZfuscator теперь коммерческий. - person Telemat; 12.06.2014

Отрывок Джеффри Рихтера очень хорош. Короче говоря, добавьте библиотеку как встроенные ресурсы и добавьте обратный вызов прежде всего. Вот версия кода (найденная в комментариях к его странице), которую я поместил в начале метода Main для консольного приложения (просто убедитесь, что все вызовы, которые используют библиотеку, находятся в другом методе, чем 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);
            }
        };
person Steve    schedule 06.11.2013
comment
Немного изменил, сделал работу, приятель! - person Sean Ed-Man; 21.11.2014
comment
Проект libz.codeplex.com использует этот процесс, но он также будет делать некоторые другие вещи, например, управлять обработчиком событий. для вас и некоторого специального кода, чтобы не нарушать каталоги управляемой платформы расширяемости ( сам этот процесс сломался бы) - person Scott Chamberlain; 23.02.2015
comment
Замечательно!! Спасибо @Steve - person Ahmer Afzal; 03.08.2019

Чтобы расширить @ Bobby 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>
person Josh    schedule 07.03.2015
comment
Синтаксис IL-Repack изменился, проверьте README.md, который находится в связанном репозитории github (github.com/peters/ILRepack.MSBuild.Task). Этот способ был единственным, который у меня сработал, и я смог использовать подстановочный знак, чтобы сопоставить все библиотеки DLL, которые я хотел включить. - person Seabass77; 26.07.2019

.NET Core 3.0 изначально поддерживает компиляцию в один EXE-файл.

Эта функция включается при использовании следующего свойства в файле проекта (.csproj):

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

Это делается без каких-либо внешних инструментов.

Дополнительную информацию см. В моем ответе на этот вопрос.

person Marcell Toth    schedule 01.09.2019
comment
Что насчет .NET 5, поддерживает ли он это? - person Orace; 17.03.2021
comment
Вам также необходимо добавить идентификатор среды выполнения: win10-x64 или linux-x64, например: ‹RuntimeIdentifier› linux-x64 ‹/RuntimeIdentifier› - person Nime Cloud; 02.06.2021

Вы можете добавить библиотеки DLL в качестве встроенных ресурсов, а затем попросить вашу программу распаковать их в каталог приложения при запуске (после проверки, есть ли они уже там).

Однако файлы установки создать настолько легко, что я не думаю, что это того стоит.

РЕДАКТИРОВАТЬ: этот метод будет легко использовать со сборками .NET. С библиотеками DLL, отличными от .NET, было бы намного больше работы (вам нужно было бы выяснить, где распаковать файлы, зарегистрировать их и т. Д.).

person MusiGenesis    schedule 09.10.2008
comment
Вот отличная статья, объясняющая, как это сделать: codeproject .com / Articles / 528178 / Load-DLL-From-Embedded-Resource - person bluish; 09.09.2015

Еще один продукт, который может с этим элегантно справиться, - это SmartAssembly, доступный по адресу SmartAssembly.com. Этот продукт, помимо объединения всех зависимостей в единую DLL, (необязательно) обфусцирует ваш код, удаляет лишние метаданные, чтобы уменьшить итоговый размер файла, а также может фактически оптимизировать IL для повышения производительности во время выполнения.

Существует также какая-то функция глобальной обработки исключений / отчетов, которую он добавляет в ваше программное обеспечение (при желании), что может быть полезно. Я считаю, что у него также есть API командной строки, поэтому вы можете сделать его частью процесса сборки.

person Nathan    schedule 09.10.2008

Ни подход ILMerge, ни обработка события AssemblyResolve Ларсом Холмом Дженсеном не будут работать для хоста плагина. Скажем, исполняемый файл H загружает сборку P динамически и обращается к ней через интерфейс IP, определенный в отдельной сборке. Чтобы встроить IP в H, потребуется небольшая модификация кода Ларса:

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.

person Ant_222    schedule 09.10.2013
comment
Извините за то, что разместил это как ответ, а не как комментарий. Я не имею права комментировать чужие ответы. - person Ant_222; 09.10.2013

Следующий метод НЕ ИСПОЛЬЗУЙТЕ внешние инструменты и АВТОМАТИЧЕСКИ включайте все необходимые DLL (никаких действий вручную не требуется, все делается при компиляции)

Я читал здесь много ответов, в которых говорилось об использовании методов ILMerge, ILRepack или Джеффри Ритчера, но ни один из них не работал с приложениями WPF < / em> И пользоваться им было непросто.

Когда у вас много DLL, может быть сложно вручную включить ту, которая вам нужна в исполняемый файл. Лучший метод, который я нашел, был объяснен Wegged здесь, в StackOverflow

Копия вставила здесь его ответ для ясности (все кредиты Wegged)


1) Добавьте это в свой .csproj файл:

<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>

2) Сделайте так, чтобы ваш Main Program.cs выглядел так:

[STAThreadAttribute]
public static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
    App.Main();
}

3) Добавьте метод OnResolveAssembly:

private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    AssemblyName assemblyName = new AssemblyName(args.Name);

    var path = assemblyName.Name + ".dll";
    if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);

    using (Stream stream = executingAssembly.GetManifestResourceStream(path))
    {
        if (stream == null) return null;

        var assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        return Assembly.Load(assemblyRawBytes);
    }
}
person Ludovic Feltz    schedule 16.07.2020
comment
Можете ли вы объяснить тест CultureInfo, есть ли подпапки en-us или fr-fr? Это DestinationSubDirectory? - person Orace; 17.03.2021

Это может показаться упрощенным, но WinRar дает возможность сжимать группу файлов в самораспаковывающийся исполняемый файл.
Он имеет множество настраиваемых параметров: конечный значок, извлечение файлов по заданному пути, файл для выполнения после извлечения, собственный логотип / text для всплывающего окна, отображаемого во время извлечения, отсутствие всплывающего окна, текст лицензионного соглашения и т. д.
Может быть полезно в некоторых случаях.

person Ivan Ferrer Villa    schedule 20.01.2015
comment
В самой Windows есть аналогичный инструмент под названием iexpress. Вот руководство - person Ivan Ferrer Villa; 20.01.2015

Я использую компилятор 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.

Затем добавьте вызывающий преобразователь сборки в Main:

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

    }

Я называю сценарий vbs так, чтобы он соответствовал имени файла .cs (например, 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)
person Mark Llewellyn    schedule 28.04.2016

Возможно, но не так просто, создать гибридную собственную / управляемую сборку на C #. Если бы вы использовали C ++ вместо этого, было бы намного проще, поскольку компилятор Visual C ++ может создавать гибридные сборки так же легко, как и все остальное.

Если у вас нет строгих требований к созданию гибридной сборки, я согласен с MusiGenesis, что на самом деле это не стоит того, чтобы делать с C #. Если вам нужно это сделать, возможно, лучше подумайте о переходе на C ++ / CLI.

person Chris Charabaruk    schedule 09.10.2008

Как правило, для выполнения слияния сборок, как вы описываете, вам понадобится какой-то инструмент для пост-сборки. Существует бесплатный инструмент под названием Eazfuscator (eazfuscator.blogspot.com/), предназначенный для изменения байт-кода, который также обрабатывает слияние сборок. Вы можете добавить это в командную строку после сборки с помощью Visual Studio, чтобы объединить ваши сборки, но ваш опыт будет варьироваться из-за проблем, которые возникнут в любых сценариях слияния нетривиальных сборок.

Вы также можете проверить, имеет ли NANT возможность объединять сборки после сборки, но я недостаточно хорошо знаком с NANT, чтобы сказать, встроена ли эта функция или нет.

Также существует множество подключаемых модулей Visual Studio, которые будут выполнять слияние сборок как часть сборки приложения.

В качестве альтернативы, если вам не нужно, чтобы это выполнялось автоматически, существует ряд инструментов, таких как ILMerge, которые объединяют сборки .net в один файл.

Самая большая проблема, с которой я столкнулся при слиянии сборок, - это то, используют ли они какие-либо похожие пространства имен. Или, что еще хуже, ссылаться на разные версии одной и той же dll (мои проблемы обычно были с файлами dll NUnit).

person wllmsaccnt    schedule 28.10.2010
comment
Eazfuscator просто позвонит IlMerge, AFAIK. - person Bobby; 28.10.2010
comment
+1 Бобби. Я должен был помнить об этом. Практически все, что делает для вас Eazfucator, - это абстрагирование фактических вызовов ILMerge с помощью более общего файла конфигурации. - person wllmsaccnt; 29.10.2010