Xamarin.Mac/アクセシビリティの許可の有無を取得する

提供: MonoBook
< Xamarin.Mac
2015年10月15日 (木) 04:32時点における153.220.4.222 (トーク)による版
ナビゲーションに移動 検索に移動

Mac OS XのGlobal Event Monitorなどの一部のAPIシステム環境設定にあるアクセシビリティの許可がされていない状態では機能しない。

恐ろしいことにアクセシビリティの許可がされていない状態でGlobal Event MonitorなどのAPI呼び出しを行ってもスルーされるだけでエラーも何も発生しない。 この挙動は確実に半年くらいしてどんな実装だったかを忘れたころにトラブルになり、エラーも出ないのでデバッグも捗らず原因不明のバグに悩まされ、デスマーチ突入は決定的である。

これを回避するためアプリ起動時にアクセシビリティの許可がされているかを確認し、未許可であれば警告を出す必要がある。

実装:ApplicationServicesフレームワークを使用する

ApplicationServicesという標準フレームワークのAPIを叩くことで実現できる。 管理者権限は必要ない。

なお、Mac OS X 10.8Mountain Lion)以前はAXAPIEnabledメソッドを使用し、Mac OS X 10.9Mavericks)以降はAXIsProcessTrustedWithOptionsメソッドを使用するようになっており、OSのバージョンにより叩くべきメソッドが異なる。これはアクセシビリティの設定が、Mac OS X 10.8まではOSにひとつの許可設定であったが、Mac OS X 10.9からはアプリ単位の許可設定となったためだと思われる。

Xamarin.MacにはApplicationServicesフレームワーク関連のライブラリが無いようなので直接叩いてやる。

using System;
using System.Runtime.InteropServices;
using MonoMac.Foundation;

namespace PrivacyAccessibility
{
    public static class ApplicationServices
    {
        public const string DllName = "/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices";

        [DllImport(DllName)]
        public extern static bool AXIsProcessTrustedWithOptions(IntPtr option);

        [DllImport(DllName)]
        public extern static bool AXAPIEnabled();

        public static bool IsCurrentProcessTrusted()
        {
            var dic = NSDictionary.FromFile(@"/System/Library/CoreServices/SystemVersion.plist");
            var osVersion = new Version(dic["ProductVersion"].ToString());
            Console.WriteLine(osVersion);

            if (new Version(10,9) <= osVersion)
            {// 10.9 Mavericks以降
                var options = IntPtr.Zero;
                return AXIsProcessTrustedWithOptions(options);
            }
            else
            {// 10.8 Mountain Lion以前
                return AXAPIEnabled();
            }
        }
    }
}

AXIsProcessTrustedWithOptionsメソッドの引数に「AXTrustedCheckOptionPrompt」に「1」を設定して渡してやると、アプリのアクセシビリティが未許可の場合にシステム環境設定を開くかを確認するダイアログが表示される。なお、ダイアログが表示されても、そこでスレッドは止まらずに即座に戻り値「false」が返されて続行される。

var options = NSDictionary.FromObjectAndKey(new NSNumber(1), new NSString("AXTrustedCheckOptionPrompt"));
AXIsProcessTrustedWithOptions(options.Handle);

また、古いOSの場合は自前でダイアログを表示する必要がある。新旧に対応し、かつ統一感を出すには自前で警告ダイアログを実装した方がいいかもしれない。これらの理由によりアクセシビリティの許可設定の確認と未許可時の警告ダイアログの表示は別々に処理した方が良いと思われる。

関連項目

参考文献