Skip the path: stick to the StorageFile
To build user confidence, a user’s files are protected from arbitrary access by Windows Store apps. This has produced a large number of threads in the Windows Store apps Forums from developers who are used to having full access to the file system and who don’t expect to need the user’s permission to view or modify data.
Examples of these questions include:
- Why can’t I open c:\test\test.txt?
- Why can’t I get the path name from a StorageFile?
- Why can’t I get the path for the MusicLibrary?
- I have the musicLibrary capability. Why do I get access denied when I try to fopen() a file there?
- How do I reopen a file the user picked later without forcing the user to pick it again?
This protected view for files is provided by the StorageItems classes (i.e. StorageFolder and StorageFile). StorageItems are the canonical storage identifiers for Windows Store apps, not the path. Your life (and your app) will be easier if you think and work in terms of StorageItems rather than trying to convert back and forth between StorageItems and paths.
Windows Store apps run sandboxed and have very limited access to the file system. For the most part, they can directly access only their install folder and their application data folder. They do not have permission to access the file system elsewhere (see File access and permissions for more details).
Access to other locations is available only through a broker process. This broker process runs with the user’s full privileges, and it can use these privileges on the app’s behalf for locations the app has requested via capabilities, locations requested by the user via file pickers, etc. The StorageItem encapsulates this brokerage procedure so the app doesn’t need to deal with it directly.
As James briefly mentioned last week, another key property of the StorageFile is that it isn’t restricted to file system objects. For example, a StorageFile can represent a photo “file” the user selected from another app via the file picker contract . The app deals with the StorageFile and doesn’t need to know or care if the data originated on disk or in another app. Conversely, apps which demand to know where the data originated can run into trouble: the StorageFile may not have a Path or may have a non file-system path.
Two common reasons (beyond habit) that people often try to convert StorageFiles to paths are to interact with legacy code which requires a path and to remember the file so it can be used later.
Legacy code dependencies can be tricky to work around. If the library can be limited to use only the app’s own files then it can directly read files in the install folder and it can read and write to the application data folders. If it needs to work on user chosen files or data from libraries then it will need some modification. The best way would be to provide a streaming interface for the library in addition to the file interface. This will let the app get the StorageFile and pass a stream to its contents. If the library doesn’t have such an interface and you cannot add one then you will need to copy the StorageFile contents into the application data folder (likely in the TemporaryFolder) and then pass the path to the temporary copy to the library.
Converting a StorageFile to a path to remember it for later is often workable, but you will need to have a fallback identifier for path-less StorageItems. The Windows.Storage.AccessCache namespace provides classes to remember StorageItems so they can be reloaded later without requiring the user to go through the picker. The StorageApplicationPermissions.FutureAccessList provides random access to items that have been previously used. The MostRecentlyUsedList allows keeping a list of recently used locations. See How to track recently used files and folders and the File picker sample and the File access sample).
So with that understanding, let’s take a quick look at those questions:
- Why can’t I load an image from c:\test\test.png?
The app doesn’t have direct access to c:\test. Instead use a file in its application data or install directory, or let the user choose a file via a file picker.
- Why can’t I get the path name from a StorageFile?
The StorageFile refers to a non file-system location.
- Why can’t I get the path for the MusicLibrary?
The StorageFile refers to a non file-system location. The Music Library is a Windows Shell Library which collects files from multiple directories in a single folder (by default the library merges a public and a private directory, but the user can add others).
- I have the musicLibrary capability. Why do I get access denied when I try to open a file there?
The musicLibrary capability does not grant the app direct file system access. The can request a StorageFolder from KnownFolders.MusicLibrary to gain brokered access to the StorageFiles in the library. Once you have the StorageFile for the item you want you can use its stream to initialize a MediaElement.
- How do I reopen a file the user picked later without forcing the user to pick it again?
You can cache access to StorageFile in the FutureAccessList and then retrieve it automatically in a future instance of the app.
I hope this helps you understand the brokerage model and how using StorageItems directly instead of trying to rely on paths will help make your coding easier!
You can follow me @RobCap . Our team’s hashtag is #WSDevSol.
Comments
Anonymous
December 11, 2012
The broker approach is bad API design for legacy code imho. I understand the StorageItems philosophy and that they can represent not actual files but rather streams of data. But Apple did A LOT better by providing native file access once the user has given your app permission to (certain folders). There you can use the whole CRT code to work with files. I really have to wonder why MSFT, after all those years, ditched backward compatibility.Anonymous
January 20, 2013
I second philk's comment here. It would be nice if we could get some kind of unique file path via the OpenFilePicker/IStorageFile that would allow me to open the file through the broker but still re-use the existing native file-api's. Ie. something like "broker://guid/fileid". Because I have to rely on IStorageFile now, I have to make SIGNIFICANT changes to my cross-platform code to be able to read files on this one platform, while all other platforms continue to use a simple string for a file path.Anonymous
October 14, 2015
I am a new guy to build a windows apps. so can you give some example codes, thanks very muchAnonymous
November 16, 2015
The comment has been removedAnonymous
November 22, 2015
Thank you so much! I have been working on a cortana extension app and hit the wall of needing permissions, but didn't want to constantly intrude the user experience to ask for a file path. AccessCache is what I needed.