#.net #embedded-resource
#.net #встроенный-ресурс
Вопрос:
У меня есть решение VS, которое содержит три проекта — один создает EXE, а два других создают библиотеки DLL. Это организовано таким образом, потому что библиотеки DLL содержат код, который используется совместно с другими EXES. Когда я развертываю EXE-файл, я хочу иметь возможность просто скопировать EXE-файл в каталог bin и запустить его, без необходимости копировать дополнительные библиотеки DLL. Это особенно важно, потому что другие EXES в каталоге bin будут созданы на основе более ранних версий библиотек DLL.
Итак, я включил библиотеки DLL, созданные из двух проектов в моем EXE, в качестве встроенных ресурсов и подключил AppDomain.CurrentDomain .AssemblyResolve позволяет загружать их при запуске EXE.
И это работает, за исключением двух вещей:
- Каждый проект DLL фактически создает две библиотеки DLL, одну, когда конфигурация «Debug», и одну, когда конфигурация «Release». Было бы неплохо включить правильную сборку в правильную сборку. И
- Если библиотеки 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 определенно делает то, что указано в названии вопроса, и делает это лучше, чем то, что обсуждается в тексте вопроса. Но да, у него есть некоторые ограничения (например, сборки в смешанном режиме)