Sometimes there is a need to reference binary in C# project. It is pretty easy — we need to add reference to dll (if it is a managed one) or add it to project and copy it to output directory on build (if it is a native library). However, problems might occur when we project A references project B which in turn references external library. How do we make sure that all libraries are copied to output directory on build? And what happens if library is referenced by some Nuget package? What’s more, how do we make sure that VS chooses library matching processor architecture?

Since these scenarios are pretty common, there are existing questions on SO, e.g:
Why the native DLL is not copied to the output directory
Add native files from NuGet package to project output directory
Visual studio not copying content files from indirectly referenced project
and probably many more. In this post I describe one of possible solutions for each scenario. Let’s begin.

Native library without Nuget

First scenario is referencing native library directly in project. So, we have project A referencing library lib_x86.dll and lib_x64.dll. We also have project B referencing project A and project C referencing project B. We compile the code and run the project C. We would like to have library matching processor architecture to be deployed to C’s output directory and loaded in runtime.

First, in order to copy library to output directory I usually use extension vsSolutionBuildEvents. It allows to perform basically any command on any event, e.g., copy file after particular project is built. GUI could be a bit more responsive but it is sufficient anyway.

Having this solution we can solve first case: we simply add shell command to copy binary from A’s output directory to C’s output directory. Since we can use project’s dependent variables we don’t need to hardcode paths so this solution will work after moving project, changing Debug build to Release or after changing architecture.

Copying file is one thing, another thing is referencing correct library basing on architecture. Since we need to be able to find correct library in runtime, I usually deploy both versions of library, e.g., x86 and x64. So in my output directory I have the following files:
OutputDirectory\lib\x86\lib_x86.dll
and
OutputDirectory\lib\x64\lib_x64.dll
Now I need to be able to find correct library in runtime. Assuming that it is class LibWrapper which uses library, I use the following static constructor:

static LibWrapper()
{
	var architectureDirectory = System.Environment.Is64BitProcess ? @"lib\x64" : @"lib\x86";
	var location = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
	var dllPath = Path.Combine(location, architectureDirectory);
	string pathEnvironmentVariable = System.Environment.GetEnvironmentVariable("PATH");
	string finalPath = $"{pathEnvironmentVariable};{dllPath};";
	System.Environment.SetEnvironmentVariable("PATH", finalPath, EnvironmentVariableTarget.Process);
}

First, I check what architecture I am running on. If you are targeting .NET 2.0, you probably would prefer to check pointer size since property Is64BitProcess might not be accessible. Next, I get location of binaries using some black magic — this looks difficult but is required in order to work with unit tests using NUnit. Finally, I create path pointing to directory and add it to the PATH environment variable. You might wonder why not use SetDllDirectory but this function entirely replaces search path and I don’t want to mess with other things as there might be other mechanisms depending on DLL search path.

To sum up: I copy binary in all versions using VS extension and I find it dynamically in runtime. When the library is required, OS will take care of finding it in PATH directories and all will work fine.

Managed library without Nuget

Now we want to reference managed library. We can do that using conditional reference in csproj file:


  
    lib\x64\lib_x64.dll
  
  
    lib\x86\lib_x86.dll
  

You need to edit project file by hand and replace ordinary reference with these two. Now the correct library will be referenced during build. Of course you will still hit the problem with library not copied to output directory with indirect references which you can solve using vsSolutionBuildEvents.

Native library using Nuget

Now we want to deploy a Nuget package with embedded native library. As before, we want to choose correct library to match the architecture.

For the first part (copying library to output directory) we can use Baseclass.Contrib.Nuget.Output package. It is capable of copying specified files to output directory on build. Follow the documentation to include lib_x86.dll and lib_x64.dll and copy them to lib\x86 and lib\x64 respectively.

For the second part (loading correct library in runtime) we can use the same static constructor as in the first scenario.

Managed library using Nuget

Now we want to deploy Nuget package with correct DLL included. Unfortunately, I am not aware of any simple solution which would not require multiple Nuget packages. In theory you could use PowerShell scripts to copy correct libraries and modify csproj on the fly but I don’t find this solution easy and nice. So for now I prefer to deploy two different packages, one for x86 and another one for x64.

So the only difficult part here is how to include correct library in Nuget package. I use the following nuspec file:



  
    $id$$packageVersion$
    ...
  
  
	
  

Next, I build project in all required versions (AnyCPU, x86, x64) and run the following command to create package:

nuget pack Project.csproj -prop "Configuration=Release;Platform=AnyCPU;arch=x86;packageVersion=.x86"
nuget pack Project.csproj -prop "Configuration=Release;Platform=x64;arch=x64;packageVersion=.x64"

This creates two packages: Package.x64.nuget and Package.x86.nuget. Now I just need to batch build solution and run the scripts.

Summary

I hope this covers all possible scenarios which you might face. I am not saying that these solutions are perfect, however, for my needs they work pretty well and are simple enough to implement them without bothering with all things that might go wrong.