I have a mixed mode C++ project producing a managed dll assembly exporting some CLR classes (call it Managed.dll). This project is using a native dll, (call it Native.dll).
When I reference the Managed.dll from another project producing Client.exe, everything works as expected, except than I need to manually copy the Native.dll in the same folder as Client.exe.
If there a way to convince VS to copy locally (in the bin folder of Client.exe) not only Managed.dll but Native.dll as well?
I have tried to include Native.dll as a dependency assembly in the manifest but this didn't help.
Edit
Managed.dll is going to be a redistributable assembly. It will be installed in a folder in "C:\Program Files.....". When a developer using Visual Studio adds a reference to Managed.dll, Native.dll should be also copied in the \bin folder of his project.
There are several ways to tell the VS to copy dlls to the destination folder:
1.Add the dll as a resource of the project. And tell the VS to copy it if the dll is newer
2.Add a new project that reference to the dll project, and set the OutDir to the folder you want. This project does nothing but copy the dll.
3.Use a PostBuildEvent in vcxproj file
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
</ClCompile>
<Link>
</Link>
<PostBuildEvent>
<Command>
echo off
mkdir "$(ProjectDir)..\..\bin\$(Configuration)\"
copy "$(OutDir)xxx.dll" "$(ProjectDir)..\..\lib\$(Configuration)\"
echo on
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
4.Use a PreBuildEvent in vcxproj file
5.Use CustomBuild in vcxproj file
<ItemGroup>
<CustomBuild Include="..\..\xxx.dll">
<FileType>Document</FileType>
<Command>
call mkdir "$(OutDir)" 2>nul &
copy /Y "..\..\xxx.dll" "$(OutDir)xxx.dll"
</Command>
<Message>Copying xxx.dll to $(OutDir)\xxx.dll</Message>
<Outputs>$(OutDir)\xxx.dll</Outputs>
</CustomBuild>
</ItemGroup>
6.Use a makefile and copy dll in makefile. and use nmake to build
7.Write a bat file that do the copy job, and invoke the bat file as in 3-6
8.Use script such as python, which can also download the dll from internet. And invoke the py file as in 3-6.
9.Other build tools can help too, such as gradle
10.Make it a NuGet plugin
11.Sometimes I just write a bat, and execute the bat manually.
Update 01 (Self extract dll example):
1.Add you native dll as resource of managed dll
2.Add this init() method
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace DllSelfExtract
{
public class SelfExtract
{
public static void Init()
{
String managedDllPath = System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
String nativeDllPath = managedDllPath.Replace("file:///", "").Replace("DllSelfExtract.DLL", "TestDll.dll");
if(!File.Exists(nativeDllPath))
{
Stream dllIn = Assembly.GetExecutingAssembly().GetManifestResourceStream("DllSelfExtract.TestDll.dll");
if (dllIn == null) return;
using (Stream outFile = File.Create(nativeDllPath))
{
const int sz = 4096;
byte[] buf = new byte[sz];
while (true)
{
int nRead = dllIn.Read(buf, 0, sz);
if (nRead < 1)
break;
outFile.Write(buf, 0, nRead);
}
}
}
//LoadLibrary Here
}
}
}
3.In project that use your managed dll, invoke init() method first
SelfExtract.Init();
Update 02 (NuGet example):
1.Create a new NuGet project
2.Place the managed assemblies in the /lib directory
3.Place the non-managed shared libraries and related files in the /build subdirectory and rename all non-managed *.dll to *.dl_
4.Add a custom .targets file in the /build subdirectory with something like the following contents :
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AvailableItemName Include="NativeBinary" />
</ItemGroup>
<ItemGroup>
<NativeBinary Include="$(MSBuildThisFileDirectory)*">
<TargetPath></TargetPath>
</NativeBinary>
</ItemGroup>
<PropertyGroup>
<PrepareForRunDependsOn>
$(PrepareForRunDependsOn);
CopyNativeBinaries
</PrepareForRunDependsOn>
</PropertyGroup>
<Target Name="CopyNativeBinaries" DependsOnTargets="CopyFilesToOutputDirectory">
<Copy SourceFiles="@(NativeBinary)"
DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).dll')"
Condition="'%(Extension)'=='.dl_'">
<Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
</Copy>
<Copy SourceFiles="@(NativeBinary)"
DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).%(Extension)')"
Condition="'%(Extension)'!='.dl_'">
<Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
</Copy>
</Target>
</Project>
5.Add build rule for build folder in Package.nuspec
<files>
<file src="lib\" target="lib" />
<file src="tools\" target="tools" />
<file src="content\" target="content" />
<file src="build\" target="build" />
</files>
6.Build the package
7.In your other C# project just add this NuGet package.
Using /ASSEMBLYLINKRESOURCE option in linker properties seems to be the simplest solution. It makes Visual Studio consider the native dll as a part of the assembly. Also, according to documentation provided by Microsoft, allows the native dll to be installed in the Global Assembly Cache
To set this linker option in a Visual C++ project:
You will need a Post Build event to copy the native dll to the output folder.
Referencing the managed assembly from any other Visual Project, forces the native dll to be copied together with the managed assembly in the /bin folder.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With