.NET - 在EXE中嵌入引用的DLL,当它们是项目引用时

时间:2011-10-07 16:14:33

标签: .net embedded-resource

我的VS解决方案包含三个项目 - 一个构建EXE,另外两个构建DLL。它以这种方式组织,因为DLL包含与其他EXE共享的代码。当我部署EXE时,我希望能够将EXE复制到bin目录中并使其运行,而无需复制额外的DLL。这一点尤其重要,因为bin目录中的其他EXE将针对早期版本的DLL构建。

所以我将EXE中两个项目构建的DLL作为嵌入资源包含在内,并在EXE运行时挂钩AppDomain.CurrentDomain.AssemblyResolve加载它们。

这有效,除了两件事:

  1. 每个DLL项目实际上构建了两个DLL,一个在配置为“Debug”时,一个在配置为“Release”时。用适当的构建包含正确的构建会很好。和
  2. 如果DLL尚不存在,则构建失败。
  3. 这是第二个真正的问题。如果DLL已经存在,那么构建运行正常。但是如果缺少DLL,则构建失败。因此,添加这些依赖项只有在我首先构建没有依赖项的解决方案,然后将它们添加到其中时才有效。

    当然,这不起作用。我需要一个可以通过干净的结账方式构建的解决方案。

    那么,有什么想法吗?

3 个答案:

答案 0 :(得分: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 :(得分:1)

好的,这是问题所在。每个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的调试版或发行版,具体取决于我正在构建的内容。

答案 2 :(得分:0)

我从未使用它,但ILMerge应该能够处理这个问题。构建项目后,将可执行文件和dll传递给ILMerge,然后让它为您创建一个主程序集。

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