WindowsでiPhoneのデベロッパモードを有効にする方法

# iOS16からデベロッパモードが導入
iOS16からiPhoneで開発する際には「デベロッパモード」を有効にする必要があります。
https://qiita.com/YokohamaHori/items/d00e1786c34b4ab30638

### iOS16βではデフォルトで「設定」にデベロッパモードの項目が表示されていますが、製品版では表示されなくなったようです。

原則としてMac(XCode入り)につないで項目を表示させてから、ONにする必要があります。
（↑　この部分で何らかの命令を飛ばしている？）

## Windowsのみでデベロッパモードを有効にする

通常はiPhoneの開発をするのにMacがないなんて考えられないのですが、なんやかんやあって、WindowsのみでデベロッパモードをONにする必要が生じました。

iOSデバイス用のオープンソースライブラリである[libimobiledevice](https://github.com/libimobiledevice/libimobiledevice)では、対応してそうなissueがありますが、windows用のビルドが通らない & nuget版も長らく更新していない。

- 方針 とりあえず既存のlibimobiledeviceのDLLを叩いて、無理やりデベロッパモードを出現させる。


## コーディング
### 前提
- VisualStudioとC#を使用します。
- nugetでlibimobiledeviceをインストールします。
- 適当なクラスを作成してそのメソッドとして構築しています。(以下ではIosDeviceクラスとして作成)

``` C#
//iPhoneを識別するためのudid
private readonly string udid;
//libimobiledeviceのAPIを叩くための宣言
private IScreenshotrApi sc;
private ScreenshotrClientHandle clientHandle;

//コンストラクタ
public IosDevice()
{
    NativeLibraries.Load();
    //まずUDIDの取得
    int count = 0;
    IiDeviceApi idevice = LibiMobileDevice.Instance.iDevice;
    iDeviceError ret = idevice.idevice_get_device_list(out ReadOnlyCollection<string> udids, ref count);
    if (ret == iDeviceError.NoDevice)
    {
        throw new Exception("Connot recognize iOS devices.");
    }
    ret.ThrowOnError();
    if (udids.Count == 0)
    {
        throw new Exception("Connot recognize iOS devices.");
    }
    //２台以上接続は不可
    if (udids.Count > 1)
    {
        throw new Exception("Do not connect multiple devices.");
    }
    this.udid = udids[0];
}
```
### iOSバージョンの取得
``` C#
    //libimobiledeviceのexeファイルのCコードを、C#に改造しています
    NativeLibraries.Load();
    IiDeviceApi idevice = LibiMobileDevice.Instance.iDevice;
    iDeviceHandle deviceHandle;
    //今後、ほとんどのメソッドは、引数にoutを指定して結果を保存している(戻り値は成否)
    try
    {
        //デバイスハンドルの取得
        idevice.idevice_new(out deviceHandle, this.udid).ThrowOnError();
    }
    catch (Exception ex)
    {
        throw ex;
    }
    ILockdownApi lockdown = LibiMobileDevice.Instance.Lockdown;
    LockdownClientHandle lockdownHandle;
    try
    {
        //ロックダウンハンドルの取得
        lockdown.lockdownd_client_new_with_handshake(deviceHandle, out lockdownHandle, "AP");
    }
    catch
    {
        throw new Exception("Connot recognize iOS devices.");
    }
    //iOSのバージョンを取得
    string version;
    try
    {
        lockdown.lockdownd_get_value(lockdownHandle, null, "ProductVersion", out tested);
        tested.Api.Plist.plist_get_string_val(tested, out version);
    }
    catch(Exception ex)
    {   
        throw new Exception(ex.Message);
    }
    int majorVersion;
    //iOSのバージョン表記は〇.〇.〇
    Int32.TryParse(version.Split('.')[0], out majorVersion);
```
## iOSバージョンが16以上の場合デベロッパモードを表示

``` C#
//iOS16以上の場合は開発モード必須
if (majorVersion >= 16){
    //デベロッパモードの状態取得
    if (lockdown.lockdownd_get_value(lockdownHandle,
        "com.apple.security.mac.amfi", "DeveloperModeStatus", out PlistHandle devMode) != LockdownError.Success)
    {
        throw new Exception("Failed to acquire developer mode.");
    }
    //デベロッパモードが無効の場合、有効化
    char dev_mode_status = '\0';
    devMode.Api.Plist.plist_get_bool_val(devMode, ref dev_mode_status);
    if (dev_mode_status == '\0')
    {
        if (lockdown.lockdownd_start_service(lockdownHandle, "com.apple.amfi.lockdown",
            out LockdownServiceDescriptorHandle amfiService) != LockdownError.Success)
        {
            throw new Exception("Failed to acquire developer mode service.");
        }
        IPropertyListServiceApi pList = LibiMobileDevice.Instance.PropertyListService;
        if (pList.property_list_service_client_new(deviceHandle, amfiService,
            out PropertyListServiceClientHandle amfi) != PropertyListServiceError.Success)
        {
            throw new Exception("Failed to acquire developer status.");
        }
        PlistHandle dict = devMode.Api.Plist.plist_new_dict();
        // devMode.Api.Plist.plist_new_uintの値
        // 1 = 自動ON（パスコードなし時のみ可能）0 = 項目表示のみ
        devMode.Api.Plist.plist_dict_set_item(dict, "action", devMode.Api.Plist.plist_new_uint(0));
        if (pList.property_list_service_send_xml_plist(amfi, dict) != PropertyListServiceError.Success)
        {
            throw new Exception("Request to display DeveloperMode failed.");
        }
        if (pList.property_list_service_receive_plist(amfi, out dict) != PropertyListServiceError.Success)
        {
            throw new Exception("Request to display DeveloperMode denied.");
        }
        PlistHandle errorValue = devMode.Api.Plist.plist_dict_get_item(dict, "Error");
        if (!errorValue.IsInvalid)
        {
            devMode.Api.Plist.plist_get_string_val(errorValue, out string err);
            throw new Exception("An error occurred when changing DeveloperMode.\n" + err);
        }
        
        PlistHandle val = devMode.Api.Plist.plist_dict_get_item(dict, "success");
        char success = '0';
        if (!val.IsInvalid)
        {
            devMode.Api.Plist.plist_get_bool_val(val, ref success);
        }
        if (success == '0')
        {
            throw new Exception("Failed to display DeveloperMode.");
        }
    }
}