4

I'm trying to create a managed library and package it with Nuget. The managed library consumes another DLL written in C and makes the calls through [DllImport] P/Invoke calls. In the root of the project I have a folder structure like:

  • root/runtimes/win-x86/native/file.dll
  • root/runtimes/win-x64/native/file.dll

I am not using a .nuspec file but instead generating the Nuget package from the .csproj file from a .NET STandard 2.0 project. I'm including those files with this in the .csproj file.

<ItemGroup>
    <Content Include="runtimes\**" PackagePath="runtimes" Visible="true" />
</ItemGroup>

The nuget package has the managed DLL in the lib/netstandard2.0 directory and even has the unmanaged DLL in root/runtimes/RID/native directory. The project that is consuming this nuget package installs perfectly and builds without error.

The managed DLL the consumer will interface with is trying to use that native DLL like this

[DllImport("file_name.dll")]
private static extern IntPtr CscanHOpen(string connectionString);
//...
public void Open(string connectionString, int port) {
    _handle = CscanHOpen(connectionString, port);
}

I'm getting the "file cannot be found" error from the project installing the nuget package and when I look in the build directory I don't see that unmanaged DLL there. I've tried using a targets file to copy the unmanaged DLL to the output directory of the consuming project but it never shows up.

Logan K
  • 324
  • 2
  • 8
  • look at `obj/project.assets.json`. The two sections (json keys) at the top are `targets` and `libraries`. The libraries section lists all the files in a package, so even if your find your dlls in there, it doesn't mean anything. Find your package in the targets section of the file. It should have compile and runtime properties containing any files that NuGet selected. if your native dlls aren't listed, then the rest of the .net sdk won't know about it. The assets file should also give a hint about which RIDs exist for the project (look at direct children of the targets object) – zivkan May 15 '19 at 20:35
  • The project that is consuming the Nuget package is a .NET Framework 4.7.2 class library project (NUnit Tests). When I look in the obj folder there is no project.assets.json file. Is that only created for .NET Core/Standard projects? – Logan K May 15 '19 at 21:22
  • it's created by `ProjectReference` (PR) projects, which can be either SDK style, or traditional project files. NuGet's runtime stuff isn't supported by `packages.config` projects. I'm not sure it works with traditional projects using PR either. Best bet is to use an SDK style project (create a .NET Core project, then edit the csproj and change the target framework to `net472`) – zivkan May 15 '19 at 21:37
  • also, I'm not sure if class libraries consider runtime assets, since they're not designed to be executables. You'd be better off testing with a console app. – zivkan May 15 '19 at 21:58
  • I think you're correct. At first glance when I created a .NET Core app it copied the runtime folders from the nuget package to the build directory. I'm going to look into this a bit more either tonight or tomorrow morning and let you know if there is enough here to mark this solved. – Logan K May 15 '19 at 22:04
  • This seems to be correct. If it's a Class Library project it doesn't seem to work. However if I do a .NET Core or .NET Framework project it will copy the unmanaged library to the consuming project's output directory. – Logan K May 16 '19 at 21:45

1 Answers1

3

I keep looking back at this question because there are no answers, so I'm going to repeat what we discussed in the comments.

The runtimes feature of NuGet only works for projects using your package with PackageReference, not packages.config. In fact, despite being on the NuGet client team, I'm not actually sure if runtimes is an SDK-style project feature, or if it also works with traditional projects. Honestly, I expect it only works with SDK style projects, because it requires the rest of the build system to know how to use these assets.

Therefore the answer to your question is to test your package with a SDK-style project. All .NET Core apps are SDK style, but you can also edit the project and change <TargetFramework>netcoreapp2.2</TargetFramework> to `net48, or make the xml tag plural and the value a semi-colon delimited list of TFMs.

Also note that class libraries don't have a host, hence don't have a RID. Therefore you may not see the runtime dlls. You need to make sure your test project that uses the package is something that has a host & entry point, like a console app.

To support packages.config projects (and probably traditional projects using PackageReference) you will need to bundle your own props and targets file in the package. You said you tried it, but it didn't work, which unfortunately just means you were doing it wrong. NuGet has a convention on how you must name your props and targets file. If you got that right, then the way you implemented copying the files didn't work. You can use MSBuild's increased verbosity, or msbuild -bl coupled with the MSBuild structured log viewer to debug your targets file.

Using the runtimes feature is so uncommon I don't really have any experience with it and don't know how to support both traditional projects, while using the integrated runtimes support in SDK style projects. I suggest you'll need to make sure your targets have a condition to run only when it's not an SDK style project, but I don't know how you'd detect that.

zivkan
  • 12,793
  • 2
  • 34
  • 51
  • I'll have to look more into the props and targets file again. It wouldn't surprise me if I was doing something wrong because it felt like I was just hacking away at this. Trying anything I could find and moving onto the next if it didn't work. Thanks again. – Logan K May 17 '19 at 20:22