I am trying to get native files installed alongside a .NET 8.0 C# assembly from a NuGet package. The goal is to use P/Invoke to call APIs in a native DLL, so I have to know where that DLL is located relative to the C# application using the package, and the DLL's dependencies must be found.
I tried the runtimes
hierarchy mentioned here, but that didn't work. I put the native files in the runtimes\net8.0\native
directory in the package, and I see them listed in obj\project.assets.json
in "runtimeTargets"
, but when I build the solution, they are not copied to the output directory, so I can't find the API DLL and it wouldn't find its dependencies.
Following the advice in this answer, I tried to arrange for the native files to be installed in the output directory for a C# Console Application, but the copy step doesn't run.
In that case, my NuGet package contained the native files in the build
directory along with the following Project.targets
file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeDependencies
Include="$(MSBuildThisFileDirectory)*.*"
Exclude="$(MSBuildThisFileDirectory)*.targets"
/>
<None Include="@(NativeDependencies)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
I installed my NuGet package in the console application; there were no errors. I built the solution, but the native files were not copied/linked.
I see the Project.targets
file referenced in obj/Project.csproj.nuget.g.targets
, so seemingly there's an appropriate reference to Project.targets
, but it never runs:
<Import Project="$(NuGetPackageRoot)...\build\Project.targets" Condition="Exists('$(NuGetPackageRoot)...\build\Project.targets')" />
I set Visual Studio to run MSBuild with diagnostic level output, but see no mention of "Project.targets" or "NativeDependencies".
What have I missed?
As described here, and clarified by @Martin Ullrich, it was enough to put the native files in runtimes\win-x64\native
for my case†. I then only needed to reference the DLL via [DllImport("name")]
. (I had tried that before and couldn't seem to get it to work, hence the posted question, but there were other issues that I missed then.)
My Project.nuspec
contains the following:
<files>
...
<file src="path\*.dll" target="runtimes\win-x64\native"/>
</files>
After installing the package and building the C# console application, the native DLLs are copied to, for example, ...\bin\x64\Debug\net8.0\runtimes\win-x64\native
. The DllImport
shown above, in the .cs
file using P/Invoke, then finds the native DLL with the required APIs without needing any path information.
† I previously misapprehended net8.0
as the RID to use, but there are different RIDs for native versus managed runtimes. When I used net8.0
rather than win-x64
, I needed the native runtimes' relative pathname in my source: [DllImport("runtimes\\net8.0\\native\\name")]
.