开发者

.NET - embedding referenced DLLs in an EXE, when they are project references

I have VS solution that contains three projects - one builds an EXE and the other two build DLLs. It's organized this way because the DLLs contain code that is shared with other EXEs. When I deploy the EXE, I want to be able to just copy the EXE into a bin directory and have it run, without having to copy extra 开发者_Python百科DLLs around. This is particularly important because other EXEs in the bin directory will have been built against earlier versions of the DLLs.

So I've included the DLLs built from the two projects in my EXE as embedded resources, and hooked AppDomain.CurrentDomain.AssemblyResolve to load them when the EXE runs.

And this works, except for two things:

  1. Each DLL project actually builds two DLLs, one when the configuration is "Debug" and one when the configuration is "Release". It'd be kind of nice to include the proper build with the proper build. And
  2. If the DLLs don't already exist, the build fails.

It's the second that is the real problem. If the DLLs already exists, the build runs fine. But if the DLLs are missing, the build fails. So adding these dependencies only works if I build the solution without the dependencies first, and then add them in.

Which won't work, of course. I need a solution that will build from a clean check-out.

So, any ideas?


OK, here's the problem. Each of the DLL projects creates a copy of its DLL in its own project folder, ./bin/Release/ or ./bin/Debug/.

You can't include them as embedded resources in the EXE project, because they are not in the EXE project folder.

When the EXE project finishes building, it copies the project DLLs into its own ./bin/Release/ or .bin/Debug/. Because these copied files are in the EXE project folder, you can include them as embedded resources, except that you don't want to, because they don't exist until the build is complete, and the build won't complete if they aren't there.

The solution is to put a copy of the DLLs somewhere else in your EXE project folder, and to include those copies in your assembly as embedded resources. I put them in ./DLLs/.

And then, to eliminate the necessity of copying them by hand, I added a pre-build event:

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

Note how this will copy either the debug or the release version of the DLL, depending upon what I am building.


I found this solution in a blog post somewhere, but it was a while ago and I've lost the link.

You can add a custom target by manually editing your project file as shown below. This way automatically embeds ALL references so you don't have to manually do anything after adding/removing a reference. It also allows you to have different $(ConfigurationName) values for different projects.

<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>

The embedded resource name will be AssemblyName.dll for each of the embedded assemblies. If you want to load these dlls at runtime, you can do something similar to this:

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


I've never used it, but ILMerge should be able to handle this. After you build your project, pass your executable and dlls to ILMerge and let it create one master assembly for you.

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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜