Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to access files/folders on HoloLens with IL2CPP ?

Discussion in 'VR' started by laguerre, Jul 30, 2018.

  1. laguerre

    laguerre

    Joined:
    Mar 18, 2013
    Posts:
    4
    I have a code that is running on PC and that I am trying to port to HoloLens, with IL2CPP. Since the application requires that the user copied a file/folder hierarchy, I will have to iterate through folders, check file existence and read or write to them.

    I am using Unity 2017.4.5.

    I started with a call to:
    Code (CSharp):
    1. string picturesFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
    Actually, I also tried with other folders but I was unable to get anything but an exception.

    I dug into the IL2CPP code and found that the file "IL2CPP/libil2cpp/os/WinRT/Environment.cpp" doesn't provide a full implementation for the "GetWindowsFolderPath" function... It seems to be limited to the support of "Application.persistentDataPath". So I decided to quickly add support for other folder types:
    Code (CSharp):
    1. std::string Environment::GetWindowsFolderPath(int32_t folder)
    2. {
    3.     switch (folder)
    4.     {
    5. #if !IL2CPP_TARGET_XBOXONE
    6.         case CSIDL_APPDATA:
    7.             return GetRoamingAppDataFolder();
    8. #endif
    9.  
    10.         case CSIDL_LOCAL_APPDATA:
    11.             return GetLocalAppDataFolder();
    12.  
    13.         case CSIDL_MYMUSIC:
    14.         case CSIDL_MYPICTURES:
    15.         case CSIDL_MYVIDEO:
    16.         case CSIDL_PERSONAL:
    17.         {
    18.             ComPtr<IUserDataPathsStatics> userDataPathsStatics;
    19.             auto hr = RoGetActivationFactory(HStringReference(RuntimeClass_Windows_Storage_UserDataPaths).Get(), __uuidof(IUserDataPathsStatics), &userDataPathsStatics);
    20.             IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    21.  
    22.             ComPtr<IUserDataPaths> userDataPaths;
    23.             hr = userDataPathsStatics->GetDefault(&userDataPaths);
    24.             IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    25.  
    26.             HSTRING foundPath;
    27.             if (folder == CSIDL_MYMUSIC)
    28.             {
    29.                 hr = userDataPaths->get_Music(&foundPath);
    30.                 IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    31.             }
    32.             else if (folder == CSIDL_MYPICTURES)
    33.             {
    34.                 hr = userDataPaths->get_Pictures(&foundPath);
    35.                 IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    36.             }
    37.             else if (folder == CSIDL_MYVIDEO)
    38.             {
    39.                 hr = userDataPaths->get_Videos(&foundPath);
    40.                 IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    41.             }
    42.             else if (folder == CSIDL_PERSONAL)
    43.             {
    44.                 hr = userDataPaths->get_Documents(&foundPath);
    45.                 IL2CPP_VM_RAISE_IF_FAILED(hr, false);
    46.             }
    47.  
    48.             auto p = WindowsGetStringRawBuffer(foundPath, 0);
    49.             std::string ret = utils::StringUtils::Utf16ToUtf8(p);
    50.             WindowsDeleteString(foundPath);
    51.             return ret;
    52.         }
    53.  
    54.         default:
    55.             Exception::Raise(Exception::GetUnauthorizedAccessException(L"Failed getting the path of a special folder: Access Denied."));
    56.     }
    57. }
    I was then able to get paths to the user's special folders!

    However, I then encountered another problem with a call to "Directory.Exists(string)". The directory was reported to not be existing. Again, I dug into the IL2CPP code until I found the file "IL2CPP/libil2cpp/os/Win32/File.cpp":
    Code (CSharp):
    1.     UnityPalFileAttributes File::GetFileAttributes(const std::string& path, int *error)
    2.     {
    3.         const UTF16String utf16Path(utils::StringUtils::Utf8ToUtf16(path.c_str()));
    4.         WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
    5.  
    6.         BOOL result = ::GetFileAttributesExW((LPCWSTR)utf16Path.c_str(), GetFileExInfoStandard, &fileAttributes);
    7.         if (result == FALSE)
    8.         {
    9.             *error = Win32ErrorToErrorCode(::GetLastError());
    10.             return static_cast<UnityPalFileAttributes>(INVALID_FILE_ATTRIBUTES);
    11.         }
    12.  
    13.         *error = kErrorCodeSuccess;
    14.         return static_cast<UnityPalFileAttributes>(fileAttributes.dwFileAttributes);
    15.     }
    The "GetFileAttributesExW" method always return 0. The error code is 5, which means "access denied".

    I checked capabilities of my app and they seem to properly ask for access to the "Pictures" folder (and others). I also had a look to the permissions of my app into the HoloLens (with the latest update of Windows, RS4) about the "Pictures" folder... Still, I can't run my app.

    So my questions are:
    - does IL2CPP really support file/folder access on HoloLens?
    - how am I supposed to access files and folders on HoloLens?
     
    pettepiero likes this.
  2. laguerre

    laguerre

    Joined:
    Mar 18, 2013
    Posts:
    4
    After several tests, I have been able to access files in the "Pictures" folder only through the StorageFolder class.

    In addition, calls to File::GetFileAttributes work only on files that my application package installed. Trying to access any file outside the installation folders leads to an "Access denied" error.

    As I said, I am porting existing code to HoloLens so it becomes very complicated to imagine that I have to replace every call to System.IO.* with something from UWP (if that exists). And using the async associated mecanism will bring a lot of problems because my code is blocking when dealing with the file system...

    Since months, Unity devs annonce that IL2CPP is the way to go for development on HoloLens and that it provides the same API surface than .NET Framework. But according to my tests, working with IL2CPP does not remove the need to use awful directives like "#if ENABLE_WINMD_SUPPORT" because some .NET APIs seem to be roughly implemented due to WinRT APIs constrains.

    So what am I doing wrong? What am I supposed to do?
     
  3. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Can you submit a bug report for this issue? It looks like something we should correct on the Unity side.
     
  4. laguerre

    laguerre

    Joined:
    Mar 18, 2013
    Posts:
    4
    I filled one as case 1069609.
     
    JoshPeterson likes this.
  5. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    Thanks, we will investigate this issue.
     
  6. Carpet_Head

    Carpet_Head

    Joined:
    Nov 27, 2014
    Posts:
    256
    any news on this issue? seems to be affecting us
     
  7. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,920
    We were not able to get a reproducible test case for this issue. If you have one, please submit a bug report, and will have a look at it. Thanks!
     
  8. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    I am working on this issue as we speak. It seems to require us to wrap StorageFile APIs under normal IL2CPP file operations if regular win32 calls fail.
     
  9. AAR_team

    AAR_team

    Joined:
    Jul 6, 2018
    Posts:
    10
    Hi,
    Is there any update on this issue? It seems we are also facing a similar problem.

    Code (CSharp):
    1.  
    2. StorageFolder objectFolder = await KnownFolders.GetFolderForUserAsync(null, KnownFolderId.Objects3D);
    3.         Debug.Log("INFO: Object folder path:" + objectFolder);
    4.         List<string> fileTypeFilter = new List<string>();
    5.         fileTypeFilter.Add(".obj");
    6.         QueryOptions queryOptions = new QueryOptions(CommonFileQuery.OrderByDate, fileTypeFilter);
    7.         StorageFileQueryResult queryResult = objectFolder.CreateFileQueryWithOptions(queryOptions);
    8.         IReadOnlyList<StorageFile> files = await queryResult.GetFilesAsync();
    9.         if (files.Count > 0)
    10.             foreach (StorageFile file in files)
    11.             {
    12.                 Debug.Log("INFO file names:" + file.Name);
    13.                 Debug.Log("INFO file path:" + file.Path);
    14.                 LoadThe3Dfile( file.Path) //custom function based on Asset Store asset: Runtime OBJ Importer
    15.             }
    16.         else
    17. //Do Something
    The above code seems to print the correct path of the file (as it is able to print the names of OBJ files in Object 3D folder of HoloLens). However, when I am trying to access the file on HoloLens it returns exception: DirectoryNotFoundException: 'Could not find a part of the path'. (It works on Windows PC when running through Unity Editor). I am using Unity 2018.4.0f1 (IL2CPP scripting backend). Any help in this regard is appreciated. Thanks.
     
    Last edited: May 18, 2019
  10. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    I have fixed the issue and it's now going through review and backporting process. I expect a fix to be available in a public Unity release in coming weeks. Hold tight!
     
    AAR_team likes this.
  11. AAR_team

    AAR_team

    Joined:
    Jul 6, 2018
    Posts:
    10
    Great! Thanks for quick reply!
     
  12. Jarediavionic

    Jarediavionic

    Joined:
    Jan 6, 2017
    Posts:
    71
    I dont suppose this issues gives the following error?
    FileNotFoundException: Could not find file
    "C;\Data\Users|DefaultAccount\AppData\Local\DevelopmentFiles\Ah-64GearboxVS.Release_Win32.jared.turner\Windoes.Storage.StorageFolderNoseGearBox.xls"

    Just trying to access file via IL2CPP in Unity 2019.3.04a so I am guessing this release wasnt the fix mentioned :(
     
  13. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    is that the actual path to the file? That path has bunch of typos.

    In any case, that fix hasn't landed yet, but it should work without it if you're loading it from your app directory.
     
  14. Jarediavionic

    Jarediavionic

    Joined:
    Jan 6, 2017
    Posts:
    71
    Thanks for your reply, and good to know. Unfortunately I am trying to load a file from a different directory like Documents or Camera Roll. I have no issue loading files within the unity directory itself. Thanks anyway
     
  15. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    For that you'll need my fix. It's coming soon - it's going through release management process right.
     
    AAR_team likes this.
  16. ElmarKleijn

    ElmarKleijn

    Joined:
    May 11, 2009
    Posts:
    87
    Hi,

    Hate to be the one pushing this, but any clue when this version will be released? (And will it be a 2018.4.x release too?). Today I spend also hours trying to find a clever work around.

    As I have also noticed the same issues. Like Jarediavionic mentiones, the normal directories (persistentStorage paths) seems to work. However, those are not accessible by USB MTP. The 3D objects/videos/etc are. I was thinking of well, ... abusing those to make it easy to update data files.

    The web file manager on the Hololens does not allow creation of directories, or to upload files in batch (both for me are a mystery why not, but alas...). The FileExplorer app on the Hololens is even more limited. Therefore as alternative I was thinking to try to find zip files in the persistentStorage directory and unzip those from the Unity app on the Hololens.
     
  17. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    Yeah, I do. The fix landed to 2019.1.8f1 and 2018.4.3f1. It hasn't landed to 2019.2 or 2019.3 yet.
     
  18. ElmarKleijn

    ElmarKleijn

    Joined:
    May 11, 2009
    Posts:
    87
    Ah, good to know! Thanks for your fast reply.

    Now just need to wait a bit for it to show up on the site :).
     
  19. laguerre_theoris

    laguerre_theoris

    Joined:
    Nov 22, 2018
    Posts:
    4
    Thank you very much @Tautvydas-Zilys, I tested you work in Unity 2018.4.3 with success:
    • I was able to access a folder that was placed in the user's home,
    • I was able to access a folder in the Pictures directory.

    However, I am still unable to access the Documents folder. According to the documentation, I would have to register the extensions of all the files my app may use! See: https://docs.microsoft.com/en-us/wi...ility-declarations#restricted-capability-list. Am I wrong? The declaration
    <uap:Capability Name="documentsLibrary"/>
    doesn't seem to work to bypass file extension registration.

    In the emulator of HoloLens 2, a warning message pops-up when someone tries to use the user's home folder from the FilePicker dialog. Indeed, it seems discouraged to store anything at all in the user's home.

    I finally came to the conclusion that, if I want to let users put a data folder somewhere in the HoloLens, they have to store it anywhere (perhaps in Documents but I don't have any recommendation) and then use the FolderPicker dialog to select that folder from my app.

    So I have that question: how can I "bind" the result of a FolderPicker dialog with your implementation of parts of
    System.IO.*/Environment.SpecialFolder.*
    over UWP APIs? How can I inject the obtained StorageFolder object into the IL2CPP code so I can later access files/folders it corresponds to?
     
  20. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
  21. laguerre_theoris

    laguerre_theoris

    Joined:
    Nov 22, 2018
    Posts:
    4
    Thank you for your reply but I don't see how I can use your advice.

    It seems that this API is a kind of Dictionary that binds a StorageItem object to a token (a string). With a FolderPicker dialog, I would have to add the returned StorageFolder object into the StorageApplicationPermissions.FutureAccessList property and retrieve the StorageFolder object later through the token returned when I added it.

    My problem is that I would like to use only
    System.IO.*
    APIs, not UWP ones. In this case, if I simply call
    Directory.Exists(path)
    , IL2CPP will generate code that ends up with a call to
    UnityPalFileAttributes BrokeredFileSystem::GetFileAttributesW(const UTF16String& path, int* error)
    .

    GetFileAttributesW is here a wrapper around UWP APIs without any use of StorageApplicationPermissions.FutureAccessList; which sounds normal. But, I don't see any other place where previous StorageFile/StorageFolder objects could be retrieve, this is why I asked how to "inject" them in the IL2CPP code.
     
  22. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,644
    I had the impression that if you added stuff to FutureAccessList, you'd be able to retrieve the files by path later. Perhaps I was wrong.
     
  23. laguerre_theoris

    laguerre_theoris

    Joined:
    Nov 22, 2018
    Posts:
    4
    Actually, it seems you're right!

    I used some of the code from the following post to present a FolderPicker to the user (but without trying to make a synchronous call): https://forum.unity.com/threads/how-to-show-native-file-picker.381528/

    Then, it seems that if I add the returned StorageFolder to FutureAccessList, I maintain access to the folder until I call the Clear method or Remove(token).
     
    Last edited: Jul 4, 2019