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

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

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

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

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

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

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

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

3 ответа

Решение

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

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

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

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

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

COPY $(SolutionDir)\myDLL\bin\$(ConfigurationName)\myDLL.dll $(ProjectDir)\DLLs

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

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

Вы можете добавить пользовательскую цель, отредактировав файл проекта вручную, как показано ниже. Таким образом, автоматически внедряются ВСЕ ссылки, поэтому вам не нужно ничего делать вручную после добавления / удаления ссылки. Это также позволяет вам иметь разные $(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 для каждой из встроенных сборок. Если вы хотите загрузить эти библиотеки во время выполнения, вы можете сделать что-то похожее на это:

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

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

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

Другие вопросы по тегам