Unity IL2CPPでの「インスタンスメソッドのデリゲートとコールバックの ビルド」のエラー対応

初めに

Unity プロジェクトで IL2CPP ビルドに切り替えた際、ドラッグ時のアニメーション切り替えが正しく動作しない問題があったため原因と対応方法についてのメモになります

開発環境

  • Unity 6000.0.31f1
  • IL2CPP
  • Windows 11

原因

  1. インスタンスメソッドのデリゲートをネイティブコードに渡している
    • インスタンスメソッド(非static メソッド)のデリゲートをネイティブコード(P/Invoke)に渡すことができません。
  2. コールバックメソッドに必要な属性が付与されていない
    • IL2CPP では、マネージドメソッドをネイティブコードのコールバック関数として渡す場合、MonoPInvokeCallback 属性を付与する必要があります。

対応方法

1: コールバックメソッドを静的メソッドに変更

匿名メソッドやインスタンスメソッドを使用せず、静的(static)メソッドとしてコールバック関数を定義します。

修正前のコード

WindowsAPI.EnumWindows(delegate(IntPtr hWnd, IntPtr lParam)
{
    // コールバック処理
    return true;
}, IntPtr.Zero);

修正後のコード

WindowsAPI.EnumWindows(delegate(IntPtr hWnd, IntPtr lParam)
{
    // コールバック処理
    return true;
}, IntPtr.Zero);

2. MonoPInvokeCallback 属性を追加

静的なコールバックメソッドに、MonoPInvokeCallback 属性を付与します。この属性は、IL2CPP に対してコールバックメソッドであることを知らせ、正しくマーシャリングされるようにします。

using UnityEngine;

[AOT.MonoPInvokeCallback(typeof(WindowsAPI.EnumWindowsProc))]
private static bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam)
{
    // コールバック処理
    return true;
}

3. GCHandle を使用してデータを渡す

コールバック内で必要なデータをやり取りするために、GCHandle を使用してマネージドオブジェクトをアンマネージドコードに渡します。

  1. マネージドオブジェクトを固定する:
GCHandle handle = GCHandle.Alloc(explorerWindows);
  1. ハンドルポインタを lParam に渡す:
WindowsAPI.EnumWindows(EnumWindowsCallback, GCHandle.ToIntPtr(handle));
  1. コールバック内でハンドルを取得し、データにアクセスする:
var explorerWindows = (List<ExplorerWindowInfo>)handle.Target;
  1. 処理後にハンドルを解放する:
handle.Free();