Version: 2017.1
ユニバーサル Windowsプラットフォーム: IL2CPP スクリプトバックエンドを使用して生成したプロジェクト
ユニバーサル Windows プラットフォーム: IL2CPP スクリプティングバックエンドのデバッグ

ユニバーサル Windows プラットフォーム: IL2CPP スクリプティングバックエンドのプラグイン

現時点では、IL2CPP スクリプティングバックエンドを備えたユニバーサル Windows プラットフォームのプラグインモデルは、.NET スクリプティングバックエンドを備えたユニバーサル Windows プラットフォームよりはむしろ、他の Unity プラットフォーム (Windows スタンドアロンなど) にずっとよく似ています。

マネージドプラグイン

デフォルトでは、 IL2CPP は .NET 2.0 API 互換性レベルを対象にしています。つまり、.NET スクリプティングバックエンドと異なり、 IL2CPP は .NET 4.5 を対象とした、または Windows Runtime APIs を使用する、マネージドプラグインをサポートしていません。この互換性レベルを使用するとき、すべてのプラグインは .NET 3.5、または同等の API を対象にしなければなりません。この制限を解除したい場合は、Player settings で .NET 4.6 API 互換性レベルに変更できます。

.NET スクリプティングバックエンドとのもう 1 つの違いは、IL2CPP スクリプティングバックエンドは、Unity エディターまたはスタンドアロンプレーヤーとまったく同じ .NET API サーフェスを公開しているということです。そのため、ユニバーサル Windows プラットフォームの別の .NET API をターゲットとする別のバージョンをコンパイルする必要なく、同じプラグインを使用できます。

ネイティブプラグイン

IL2CPP スクリプティングバックエンドは、P/Invoke メカニズムを通したネイティブブラグインの使用をサポートしています。つまり、ネイティブの関数のプロトタイプを指定して呼び出すことによって、C# のコード から直接ネイティブプラグインを呼び出すことができます。

[DllImport("MyPlugin.dll")]
private static extern int CountLettersInString([MarshalAs(UnmanagedType.LPWSTR)]string str);

private void Start()
{
    Debug.Log(CountLettersInString("Hello, native plugin!"));
}

MyPlugin.dll 内でのこの関数の実装は、以下のようになります。

extern "C" __declspec(dllexport)
int __stdcall CountLettersInString(wchar_t* str)
{
    int length = 0;
    while (*str++ != nullptr)
        length++;
    return length;
}

サポートしない以下の型を除き、P/Invoke のマーシャリングルールは、公式の .NET マーシャリングのものと一致します。

  • AnsiBStr
  • BStr
  • Currency
  • SAFEARRAY
  • IDispatch
  • IUnknown
  • TBStr
  • VBByRefStr

x86 での P/Invoke 関数のデフォルトの呼び出し規約は __stdcall です。

ネイティブプラグインは、2 通りの方法で作成することができます。precompiled DLL と C++ ソースコードです。

プリコンパイルしたネイティブプラグイン

ランタイムで DLL を読み込み、関数のエントリーポイントを見つけ、呼び出すことによって、プリコンパイルしたネイティブプラグインを P/Invoke 呼び出しできます。これらの DLL は、ターゲットにする CPU アーキテクチャ用の適切な Windows SDK に対してコンパイルする必要があります。また、DLL は、Unity プロジェクトに追加するとき Plugin インスペクター上でも設定しなければいけません。

C++ ソースコードのネイティブプラグイン

C++ (.cpp) コードファイルは Unity プロジェクトに直接加えることができ、Plugin インスペクターでプラグインとして動作します。これらのC++ ファイルがユニバーサル Windows プラットフォーム、および IL2CPP スクリプティングバックエンドと互換性があるように設定されている場合は、マネージアセンブリから生成される C++ コードと共にコンパイルされます。

関数は生成された C++ コードと一緒にリンクされるので、P/Invoke を行う DLL はありません。このため、DLL の名前の代わりに Internal キーワードを使用することができます。これにより、 Internal キーワードは、関数をランタイムに読み込む代わりに、C++ リンカーに関数を解決させます。

[DllImport("__Internal")]
private static extern int CountLettersInString([MarshalAs(UnmanagedType.LPWSTR)]string str);

呼び出しはリンカーによって解決されるので、マネージドサイドで関数の宣言のエラーが発生すると、ランタイムのエラーではなくリンカーのエラーが発生します。これはまた、動的な読み込みがランタイム時に行われる必要がないことを意味し、関数は直接呼び出されます。これによって、P/Invoke 呼び出しのオーバーヘッドは著しく減少します。

P/Invoke の制限

ユニバーサル Windows プラットフォームでは、IL2CPP スクリプティングバックエンドを使用するときに dll 名(kernelbase.dll など) を指定して、特定のシステムライブラリに P/Invoke を行うことはできません。 プロジェクトの外部に存在する DLL に P/Invoke を行なおうとすると、実行時に DllNotFoundException が発生します。

ただし、 DLL の名前の代わりに **Internal キーワードを指定して、システム関数に P/Invoke を行うことは可能です。これにより、リンカーがビルド時に関数を解決します。

参考資料

Plugin インスペクター


ユニバーサル Windowsプラットフォーム: IL2CPP スクリプトバックエンドを使用して生成したプロジェクト
ユニバーサル Windows プラットフォーム: IL2CPP スクリプティングバックエンドのデバッグ