プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2020/10/08

外部 DLL を NuGet パッケージに含める方法

update2020/10/09 event_note2020/10/08 0:30

C/C++ で作成された外部の DLL を C# から扱うためのラッパーを作成して NuGet パッケージとして公開しようとしていたのですが、その外部 DLL 自体も NuGet パッケージに含める方法について調べてみました。

環境

  • Visual Studio 2017
  • .NET Core 2.2

方法

ググってみるとまさにそのものが書いてある以下のページが見つかりました。

しかし、これだけではいろいろはまったので、追記・補足しておきます。

PackagePath について

出力先となる PackagePath のディレクトリ名には lib を指定する必要があるようです。 指定しなかった場合、以下の警告が表示されました。

warning NU5100: The assembly 'foo.dll' is not inside the 'lib' folder and hence it won't be added as a reference when the package is installed into a project. Move it into the 'lib' folder if it needs to be referenced.

さらに、lib だけだと以下のエラーが表示されました。

warning NU5103: The folder 'lib/foo.dll' under 'lib' is not recognized as a valid framework name or a supported culture identifier. Rename it to a valid framework name or culture identifier.

lib の下にはフレームワーク名またはカルチャー名のディレクトリを指定する必要があるみたいです。

<ItemGroup>
  <Content Include="foo.dll" Pack="true" PackagePath="lib\netstandard2.0" />
</ItemGroup>

また、 このフレームワーク名またはカルチャー名以下の階層が、作成した DLL と同じ階層に作られるようです。
例えば、作成した DLL が sample.dll だとして、外部の DLL を以下のように設定してパッケージに含めた場合、

<ItemGroup>
  <Content Include="foo\x64\foo.dll" Pack="true" PackagePath="lib\netstandard2.0\foo\x64" />
  <Content Include="foo\x86\foo.dll" Pack="true" PackagePath="lib\netstandard2.0\foo\x86" />
</ItemGroup>

NuGet パッケージインストール後の配置は以下のようになります。

  • sample.dll
  • foo\x64\foo.dll
  • foo\x86\foo.dll

外部 DLL を出力ディレクトリにコピー

外部 DLL を出力ディレクトリにコピーするには、Visual Studio で該当の DLL を右クリックし、出力ディレクトリにコピー の項目を 新しい場合はコピーする (または 常にコピーする) に設定しておく必要があります。

手動で .csproj ファイルを修正する場合は以下を追加します。

<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

publish 時に出力先ディレクトリに外部 DLL がコピーされない

この外部 DLL を含んだ NuGet パッケージを使っているアプリケーションにおいて、dotnet publish コマンドを実行しても外部 DLL が出力先の publish ディレクトリにコピーされませんでした。

publish 時に出力ディレクトリに外部 DLL をコピーするためには、まずライブラリ側(NuGet パッケージにするほう)の .csproj ファイルに以下を追加します。

<PackageCopyToOutput>true</PackageCopyToOutput>

しかし、これを PackagePathlib を指定している箇所に指定しても上手くいきませんでした。

PackagePath を指定しなかった場合、該当のファイルはコンテンツとして追加されるようなので、そこに PackageCopyToOutput を設定すると上手くいきました。
具体的には以下のような感じです。

<Content Include="foo\x64\foo.dll" Pack="true">
  <PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
<Content Include="foo\x86\foo.dll" Pack="true">
  <PackageCopyToOutput>true</PackageCopyToOutput>
</Content>

これが一番はまりました。

最終的に作成した設定

上記を踏まえ、最終的に .csproj ファイルの設定は以下のようになりました。

<ItemGroup>
  <Content Include="foo\x64\foo.dll" Pack="true" PackagePath="lib\netstandard2.0\foo\x64" />
  <Content Include="foo\x86\foo.dll" Pack="true" PackagePath="lib\netstandard2.0\foo\x86" />
  <Content Include="foo\x64\foo.dll" Pack="true">
    <PackageCopyToOutput>true</PackageCopyToOutput>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
  <Content Include="foo\x86\foo.dll" Pack="true">
    <PackageCopyToOutput>true</PackageCopyToOutput>
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>