⚙️

PowerShell Core でバイナリ モジュール (C#) を開発するときの注意点について

に公開

はじめに

サンプルとして簡単な JSON を返すコマンドレットを持つモジュールを作成します。対象の PowerShell Core のバージョンは 6.1.0 です。

実行手順

SampleModule.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
    <PackageReference Include="System.Management.Automation" Version="6.1.0" />
  </ItemGroup>
  <ItemGroup>
    <None Update="SampleModule.psd1">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

SampleModule.psd1

@{
    RootModule = 'SampleModule.dll'
    ModuleVersion = '1.0.0'
    CmdletsToExport = "*"
}

WriteHelloWorldCommand.cs

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;

namespace SampleModule
{
    [Cmdlet("Write", "HelloWorld")]
    public class WriteHelloWorldCommand : PSCmdlet
    {
        protected override void ProcessRecord()
        {
            this.WriteObject(JsonConvert.SerializeObject(new { Message = "Hello World" }));
        }
    }
}

実行結果

dotnet publish を実行し、Import-Module でモジュールを読み込んで実行しますが、エラーが発生します。

PS C:\SampleModule> dotnet publish
PS C:\SampleModule> Import-Module "C:\SampleModule\bin\Debug\netcoreapp2.1\publish\SampleModule.psd1"
PS C:\SampleModule> Write-HelloWorld
Write-HelloWorld : Could not load file or assembly 'Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'. Could not find or load a specific file. (Exception from HRESULT: 0x80131621)
At line:1 char:2
+  Write-HelloWorld
+  ~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [Write-HelloWorld], FileLoadException
+ FullyQualifiedErrorId : System.IO.FileLoadException,SampleModule.WriteHelloWorldCommand

何が起こっているのか

PowerShell Core は自身が Json.NET をライブラリとして使用しています。PowerShell Core 6.1 系では Json.NET 11.0.2 が同梱されています。PowerShell Core を起動した時点で 11.0.2 がロードされるため、インポートしたモジュールで異なるバージョンのアセンブリを読み込もうとするとエラーが発生します。この問題を回避するには、モジュールで使用する Json.NET のバージョンを PowerShell Core が使用している Json.NET のバージョンと合わせる必要があります。

この問題は PowerShell Core 固有のものではなく、AppDomain をサポートしていない .NET Core の仕様によるものです。しかし、現在でも DLL のバージョン競合に悩まされることがあります。

GitHub でも以前から Issue が報告されていますが、現時点では解決されていません。こちらの Issue の説明によると、異なるモジュールをインポートした際にそれぞれが異なるバージョンのアセンブリを使用しているとエラーが発生します。今回のケースも原因は同様ですが、影響は大きいと考えられます。

https://github.com/PowerShell/PowerShell/issues/2083

Discussion