.NET — встраивание ссылочных DLL-файлов в EXE-файл, когда они являются ссылками на проект

#.net #embedded-resource

#.net #встроенный-ресурс

Вопрос:

У меня есть решение VS, которое содержит три проекта — один создает EXE, а два других создают библиотеки DLL. Это организовано таким образом, потому что библиотеки DLL содержат код, который используется совместно с другими EXES. Когда я развертываю EXE-файл, я хочу иметь возможность просто скопировать EXE-файл в каталог bin и запустить его, без необходимости копировать дополнительные библиотеки DLL. Это особенно важно, потому что другие EXES в каталоге bin будут созданы на основе более ранних версий библиотек DLL.

Итак, я включил библиотеки DLL, созданные из двух проектов в моем EXE, в качестве встроенных ресурсов и подключил AppDomain.CurrentDomain .AssemblyResolve позволяет загружать их при запуске EXE.

И это работает, за исключением двух вещей:

  1. Каждый проект DLL фактически создает две библиотеки DLL, одну, когда конфигурация «Debug», и одну, когда конфигурация «Release». Было бы неплохо включить правильную сборку в правильную сборку. И
  2. Если библиотеки DLL еще не существуют, сборка завершается с ошибкой.

Это вторая реальная проблема. Если библиотеки DLL уже существуют, сборка выполняется нормально. Но если библиотеки DLL отсутствуют, сборка завершается неудачно. Таким образом, добавление этих зависимостей работает только в том случае, если я сначала создаю решение без зависимостей, а затем добавляю их.

Что, конечно, не сработает. Мне нужно решение, которое будет построено из чистого извлечения.

Итак, есть идеи?

Комментарии:

1. Щелкните правой кнопкой мыши ваш EXE-проект, выберите «Зависимости проекта».

Ответ №1:

Хорошо, вот в чем проблема. Каждый из проектов DLL создает копию своей библиотеки DLL в своей собственной папке проекта, ./bin/Release/ или ./bin/Debug/.

Вы не можете включить их в качестве встроенных ресурсов в EXE-проект, потому что их нет в папке EXE-проекта.

Когда EXE-проект завершает сборку, он копирует библиотеки DLL проекта в свой собственный ./bin/Release/ или .bin/Debug/ . Поскольку эти скопированные файлы находятся в папке проекта EXE, вы можете включить их в качестве встроенных ресурсов, за исключением того, что вы этого не хотите, потому что они не существуют до завершения сборки, и сборка не будет завершена, если их там нет.

Решение состоит в том, чтобы поместить копию библиотек DLL в другое место в папке вашего EXE-проекта и включить эти копии в вашу сборку в качестве встроенных ресурсов. Я помещаю их в ./DLL/.

А затем, чтобы исключить необходимость их копирования вручную, я добавил событие предварительной сборки:

 COPY $(SolutionDir)myDLLbin$(ConfigurationName)myDLL.dll $(ProjectDir)DLLs
  

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

Ответ №2:

Я нашел это решение где-то в сообщении в блоге, но это было давно, и я потерял ссылку.

Вы можете добавить пользовательскую цель, вручную отредактировав файл проекта, как показано ниже. Этот способ автоматически встраивает ВСЕ ссылки, поэтому вам не нужно ничего делать вручную после добавления / удаления ссылки. Это также позволяет вам иметь разные $(ConfigurationName) значения для разных проектов.

 <Project>

  ...

  <!-- Custom target - this includes all dll references as embedded resources during build. -->
  <Target Name="AfterResolveReferences">
    <ItemGroup>
      <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
        <LogicalName>%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
      </EmbeddedResource>
    </ItemGroup>
  </Target>
</Project>
  

Имя встроенного ресурса будет AssemblyName.dll для каждой из встроенных сборок. Если вы хотите загрузить эти библиотеки DLL во время выполнения, вы можете сделать что-то похожее на это:

 private void LoadAssemblyFromResource(string assemblyName)
{
    if (!assemblyName.EndsWith(".dll"))
        assemblyName  = ".dll";
    Assembly executingAssembly = Assembly.GetExecutingAssembly();
    using (Stream stream = executingAssembly.GetManifestResourceStream(assemblyName))
    {
        if (stream == null)
            throw new ArgumentException("Embedded assembly not found: "   assemblyName, "assemblyName");
        byte[] assemblyRawBytes = new byte[stream.Length];
        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
        Assembly.Load(assemblyRawBytes);
    }
}
  

Комментарии:

1. Может быть, это был этот пост в блоге с расширенным решением — habrahabr.ru/post/126790 (на русском языке)

2. Также может быть это с помощью примера WPF: digitallycreated.net/Blog/61 /…

Ответ №3:

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

http://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx

Комментарии:

1. ILMerge вообще не обрабатывает это. Что делает ILMerge, так это выделяет содержимое нескольких сборок и создает новую сборку, содержащую их. Это работает только некоторое время.

2. @Jeff: ILMerge определенно делает то, что указано в названии вопроса, и делает это лучше, чем то, что обсуждается в тексте вопроса. Но да, у него есть некоторые ограничения (например, сборки в смешанном режиме)