0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CoreBluetoothForUnityAdvent Calendar 2023

Day 18

【Unity】ネイティブプラグインからのコールバックでインスタンスメソッドを呼ぶ

Last updated at Posted at 2023-12-17

本記事について

この記事は CoreBluetoothForUnity Advent Calendar 2023 の18日目の記事です。

ネイティブプラグインにおいてデリゲートを使う場合 C# 側では呼び出す静的メソッドに対して MonoPInvokeCallback 属性を付ける必要があります。

Unity の Building plug-ins for iOS のドキュメントの Using delegate の項目にも記載されています。

インスタンスメソッドを渡すことはできないため、インスタンスメソッドを呼んで欲しかったら静的メソッドを経由して呼び出す必要があります。

その方法として以下の3つを考えました。

  1. C# のオブジェクトに紐づくアンマネージドオブジェトのポインタを引数に渡す
  2. デリゲート登録時にコールバックを呼び出すインスタンスを渡す。
  3. クラスに ID を付与して、その ID を引数に渡す

このうち 1 がメンバ変数増やす必要がなくて簡単だったためこの方法を選択しました。
開発する上で問題は発生しませんでした。

この方法の具体的なやり方を説明します。

環境

  • CoreBluetoothForUnity 0.4.6

コード

見た方が理解が早いと思うため、コードを先に示します。

INativeCentralManagerDelegate

CBCentralManager.cs
    internal interface INativeCentralManagerDelegate
    {
        // 呼び出したいメソッド
    }

CoreBluetoothForUnity ではネイティブプラグインから読んでほしいメソッドはインタフェースで定義しています。
インタフェースで定義することで依存切ってるのはドキュメント生成の都合のため、本記事の内容とはほぼ関係ありません。
このインタフェースを実装したクラスのメソッドを呼びたいとだけ理解していただければ大丈夫です。

それを踏まえて以下のクラスでインスタンスメソッドの呼び出しを実装しています。

NativeCentralManagerCallbacks

NativeCentralManagerCallbacks.cs

    internal class NativeCentralManagerCallbacks : INativeCentralManagerCallbacks
    {
        readonly static Dictionary<IntPtr, INativeCentralManagerDelegate> s_centralManagerDelegateMap = new Dictionary<IntPtr, INativeCentralManagerDelegate>();

        public void Register(SafeNativeCentralManagerHandle handle, INativeCentralManagerDelegate centralManagerDelegate)
        {
            s_centralManagerDelegateMap[handle.DangerousGetHandle()] = centralManagerDelegate;
            NativeMethods.cb4u_central_manager_register_handlers(
                handle,
                DidConnect,
                DidDisconnectPeripheral,
                DidFailToConnect,
                DidDiscoverPeripheral,
                DidUpdateState
            );
        }
...
        static INativeCentralManagerDelegate GetDelegate(IntPtr centralPtr)
        {
            return s_centralManagerDelegateMap.GetValueOrDefault(centralPtr);
        }

        [AOT.MonoPInvokeCallback(typeof(NativeMethods.CB4UCentralManagerDidConnectHandler))]
        static void DidConnect(IntPtr centralPtr, IntPtr peripheralIdPtr)
        {
            GetDelegate(centralPtr)?.DidConnect(Marshal.PtrToStringUTF8(peripheralIdPtr));
        }
  1. 事前に静的な辞書 Dictionary<IntPtr, INativeCentralManagerDelegate> を用意しておく
  2. MonoPInvokeCallback 属性を付けた静的メソッドが呼び出される。
  3. 静的な辞書からアンマネージドオブジェクトのポインタをキーにしてインスタンスを取得する

おわりに

ネイティブプラグインからのコールバックでインスタンスメソッドを呼ぶ方法を説明しました。
IntPtr をキーにするのはいかがなものか..とは思うものの、理想通りに動いてしまっているのでまぁいいかとしています。
こっちのほうがいいよ!等あればぜひ教えてください!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?