仮想と物理とエトセトラ

xRや物理とかごった煮の備忘録的技術ブログ

HoloLens2のファイルIOを確認する (その1 フォルダ、ファイル作成)

今回は、自分自身の備忘録もかねて、HoloLens2のファイルIOを確認します。
2回ほどに分けて、フォルダ、ファイル作成と、動画や画像、テキストの読み込みを各フォルダから行えるかどうか確認します。

このあたりの情報は、HoloLens2出てすぐのものも多く、最新OSバージョンで挙動が変わっているものもあるかもしれないので、まとめてみます。

その1では、各フォルダにフォルダ、ファイル作成を行えるかどうか確認します。
対象は、KnownFoldersと、ApplicationData中のアプリ固有のフォルダです。

docs.microsoft.com

docs.microsoft.com

1. 準備

まずは、確認を行うための準備の内容です。
フォルダ、ファイル作成用のスクリプトを作成しました。
ファイル作成については、作成したファイルの読み込みも行っています。

  • StorageControlCheck.cs
クリックで展開
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;

#if WINDOWS_UWP
using Windows.Storage;
#endif

public class StorageControlCheck : MonoBehaviour
{
    public async void EachStorageTest()
    {
        // https://docs.microsoft.com/ja-jp/uwp/api/windows.storage.knownfolders?view=winrt-19041
        // https://docs.microsoft.com/ja-jp/windows/uwp/get-started/fileio-learning-track
#if WINDOWS_UWP
        StorageTest(KnownFolders.AppCaptures);
        await Task.Delay(1000);
        StorageTest(KnownFolders.CameraRoll);
        await Task.Delay(1000);
        StorageTest(KnownFolders.DocumentsLibrary);
        await Task.Delay(1000);
        StorageTest(KnownFolders.MediaServerDevices);
        await Task.Delay(1000);
        StorageTest(KnownFolders.MusicLibrary);
        await Task.Delay(1000);
        StorageTest(KnownFolders.Objects3D);
        await Task.Delay(1000);
        StorageTest(KnownFolders.PicturesLibrary);
        await Task.Delay(1000);
        StorageTest(KnownFolders.Playlists);
        await Task.Delay(1000);
        StorageTest(KnownFolders.RecordedCalls);
        await Task.Delay(1000);
        StorageTest(KnownFolders.RemovableDevices);
        await Task.Delay(1000);
        StorageTest(KnownFolders.SavedPictures);
        await Task.Delay(1000);
        StorageTest(KnownFolders.VideosLibrary);
        await Task.Delay(1000);
        StorageTest(ApplicationData.Current.LocalFolder);
        await Task.Delay(1000);
        StorageTest(ApplicationData.Current.LocalCacheFolder);
        await Task.Delay(1000);
        StorageTest(ApplicationData.Current.RoamingFolder);
        await Task.Delay(1000);
        StorageTest(ApplicationData.Current.TemporaryFolder);
        await Task.Delay(1000);
#endif

    }

#if WINDOWS_UWP
    private async void StorageTest(StorageFolder storageFolder)
    {
        Debug.Log($"[{storageFolder.DisplayName}] storageFolder.Path:[{storageFolder.Path}]");
        Debug.Log($"MakeDir result about {storageFolder.DisplayName} is {await MakeDir(storageFolder, "test")}");
        Debug.Log($"MakeFile result about {storageFolder.DisplayName} is {await MakeFile(storageFolder, "test.txt")}");
    }
    
    
    private async Task<bool> MakeDir(StorageFolder storageFolder, string folderName)
    {
        try
        {
            StorageFolder targetDir = await storageFolder.CreateFolderAsync(folderName);
        
            Debug.Log($"[{storageFolder.DisplayName}] targetDir.Path:[{targetDir.Path}]");
            return true;
        }
        catch (System.Exception e)
        {
            
            Debug.Log($"[{storageFolder.DisplayName}] To make {storageFolder.Path + "/" + folderName}, raise exception {e}");
            return false;
        }
        
    }
    private async Task<bool> MakeFile(StorageFolder storageFolder, string fileName)
    {
        try
        {
            StorageFile targetFile = await storageFolder.CreateFileAsync(fileName);
            Debug.Log($"[{storageFolder.DisplayName}] targetDir.Path:[{targetFile.Path}]");
            await FileIO.WriteTextAsync(targetFile, "test");
            StorageFile readFile = await storageFolder.GetFileAsync(fileName);
            Debug.Log($"[{storageFolder.DisplayName}] read file contetns:{await FileIO.ReadTextAsync(readFile)}");
            
            return true;
        }
        catch (System.Exception e)
        {
            
            Debug.Log($"[{storageFolder.DisplayName}] To make {storageFolder.Path + "/" + fileName}, raise exception {e}");
            return false;
        }
        
    }
#endif
}

EachStorageTestメソッドをボタンから呼ぶことで、作成を実施します。
次に、Project SettingPlayerPublishing SettingCapabilitiesから各フォルダへのアクセス権を付与します。
今回付与した権限とその内容の対応は以下です。

  • MusicLibrary : Musicフォルダ配下
  • PicturesLibrary : Pictureフォルダ配下(カメラロールなど)
  • RemovableStorage : リムーバブルストレージへのアクセス
  • VideosLibrary : Videoフォルダ配下
  • Objects3D : Objects3Dフォルダ配下
  • RecordedCallsFolder : RecordedCallsフォルダ配下

なお、Documentsフォルダへの権限は、Unityから与えることはできません。
UnityでビルドしてVisualStudioソリューションファイルを作成した後、Package.appxmanifest </Capabilities>の前に<DeviceCapability Name="documentsLibrary" />を追加します。

  <Capabilities>
    <Capability Name="internetClient" />
    <uap:Capability Name="musicLibrary" />
    <uap:Capability Name="picturesLibrary" />
    <uap:Capability Name="removableStorage" />
    <uap:Capability Name="videosLibrary" />
    <uap:Capability Name="objects3D" />
    <uap2:Capability Name="spatialPerception" />
    <mobile:Capability Name="recordedCallsFolder" />
    <DeviceCapability Name="microphone" />
    <DeviceCapability Name="gazeinput" />
    <DeviceCapability Name="documentsLibrary" />    <===== 追加
  </Capabilities>

2. 結果

Windows Holographic for Business OSのバージョン21H1, OSビルド 20348.1014の時の結果は以下です。
今回作成したファイルはテキストファイルでしたが、Package.appxmanifestファイルの種類の関連付けは特に設定しなくても問題なくファイルの作成、読み込みができました。

StorageFolder 表示名取得 パス取得 ディレクトリ作成 ファイル作成 (ファイル読み込み含む) 補足
KnownFolders.AppCaptures キャプチャ U:\USERS\[ユーザ名]\Videos\Captures ○       ○      
KnownFolders.CameraRoll カメラ ロール U:\USERS\[ユーザ名]\Pictures\Camera Roll ×       ○       ディレクトリ作成のみ不可
KnownFolders.DocumentsLibrary ドキュメント 取得できず ○       ○       フォルダ、ファイル作成時のStorageFolder/Fileからはパス取得可。 U:\USERS\[ユーザ名]\Documents\
KnownFolders.MediaServerDevices Media Servers 取得できず ×       ×       Unspecified error。対象が未接続のためと思われる。
KnownFolders.MusicLibrary ミュージック 取得できず ○       ○       フォルダ、ファイル作成時のStorageFolder/Fileからはパス取得可。 U:\USERS\[ユーザ名]\Music\
KnownFolders.Objects3D 3D オブジェクト U:\USERS\[ユーザ名]\3D Objects ○       ○      
KnownFolders.PicturesLibrary ピクチャ 取得できず ○       ○       フォルダ、ファイル作成時のStorageFolder/Fileからはパス取得可。 U:\USERS\[ユーザ名]\Pictures\
KnownFolders.Playlists プレイリスト U:\USERS\[ユーザ名]\Music\Playlists ○       ○      
KnownFolders.RecordedCalls 録音した通話 U:\USERS\[ユーザ名]\Recorded Calls ○       ○      
KnownFolders.RemovableDevices Removable Storage Devices 取得できず ×       ×       Unspecified error。対象が未接続のためと思われる。
KnownFolders.SavedPictures 保存済みの写真 U:\USERS\[ユーザ名]\Pictures\Saved Pictures ○       ○      
KnownFolders.VideosLibrary ビデオ 取得できず ○       ○       フォルダ、ファイル作成時のStorageFolder/Fileからはパス取得可。 U:\USERS\[ユーザ名]\Videos\
ApplicationData.Current.LocalFolder LocalState U:\Users\[ユーザ名]\AppData\Local\Packages\[アプリフォルダ]\LocalState ○       ○      
ApplicationData.Current.LocalCacheFolder LocalCache U:\Users\[ユーザ名]\AppData\Local\Packages\[アプリフォルダ]\LocalCache ○       ○      
ApplicationData.Current.RoamingFolder RoamingState U:\Users\[ユーザ名]\AppData\Local\Packages\[アプリフォルダ]\RoamingState ○       ○      
ApplicationData.Current.TemporaryFolder TempState U:\Users\[ユーザ名]\AppData\Local\Packages\[アプリフォルダ]\TempState ○       ○      

カメラロールのみ、ファイルの作成はできましたが、フォルダの作成はできませんでした。
ほかの多くのフォルダについては、ディレクトリ、ファイルの作成ができることが確認できました。
フォルダに対して権限を与えれば、アプリからファイル、フォルダのやり取りが比較的容易にできることがわかりました。

次回は、実際にUnity上で動画や画像、テキストの読み込みを比較していきます。
※ 今回初めて表を使ってみましたが、なんか見づらいですね。。。どうにかできないものか。。。