Chapter 5. Storage files and folders

In this chapter, you’ll learn how Windows Store apps access storage files and folders. We’ll start by exploring how apps can access read-only resources such as images, music, and videos embedded in their package files. Then we’ll show how your app can access its own private, per-user data folders to store package-specific data. Of course, apps can access files in many other storage locations, such as the user’s documents and pictures libraries, removable media, and network-shared folders. For security reasons, accessing some of these locations requires either user interaction or that you, as a developer, enable settings in your app package’s manifest file.

This chapter focuses on navigating through files and folders, obtaining their properties and thumbnail images, and performing rich file queries. But this chapter does not address how to read and write a file’s contents; this is discussed in Chapter 6.

The WinRT storage object model

Figure 5-1 shows the relationship between the main WinRT interfaces and classes you need to understand to work effectively with storage files and folders. This object model scares many developers when they first see it because it is much more complex than what is available in other developer platforms. However, I personally love this object model because it is well segmented and compartmentalized; all members were carefully placed, and new features not available on other platforms are prominently exposed. Note that I am excluding some less important interfaces from Figure 5-1 to simplify the discussion.

Let’s now walk through the object model to understand it. The IStorageItem interface is the core of the model. This interface exposes members that operate on both files and folders. For example, you can rename and delete both files and folders. They also share several properties, such as Name, Path, DateCreated, and Attributes.

IStorageFolder inherits from IStorageItem and adds members that are specific to folders; StorageFolder is the concrete class implementing these two interfaces’ members. Similarly, IStorageFile also inherits from IStorageItem and adds members that are specific to files; StorageFile is the concrete class implementing these two interfaces’ members.

The WinRT object model for working with storage files and folders.

Figure 5-1. The WinRT object model for working with storage files and folders.

The IStorageItemProperties interface defines members that expose a storage item’s properties, such as thumbnail image, display name, and display type. Because files and folders both have properties, both the StorageFolder and StorageFile concrete types implement this interface. We’ll discuss how your app can work with properties in this chapter’s Storage item properties section.

Finally, as we mentioned in the introduction, WinRT exposes a rich set of operations for querying files and folders. The IStorageFolderQueryOperations interface exposes this functionality. Because you’ll initiate a query via some root folder, only the StorageFolder class implements this interface; StorageFile does not.

A StorageFile object represents an actual file on disk. However, a StorageFolder object does not necessarily represent a folder on a disk. A StorageFolder object can also refer to a virtual folder, such as the user’s pictures library. A library is a virtual folder, and its contents come from many subdirectories spread across fixed disks or disks attached to different machines on the network. When a StorageFolder object refers to a virtual folder, its Path property will be an empty string (“”). In WinRT, virtual folders are first-class citizens in the storage object model. This enables some very rich and powerful scenarios, such as querying and filtering, as shown in this chapter’s Performing file and folder queries section.

As you saw in Chapter 1 all WinRT APIs that perform I/O operations are exposed as XxxAsync methods. Making I/O APIs asynchronous ensures that they don’t block your app’s UI thread, allowing the thread to continue processing user input so that it remains responsive to the user.

To simplify Figure 5-10, all the static methods defined by the StorageFolder and StorageFile concrete classes are not shown. However, you should be aware that these methods do exist and they are all factory methods that simply return IAsyncOperation<StorageFile> or IAsyncOperation<StorageFolder> objects.

Package and user files

As your app runs, it can access various files that are classified as follows:

  • Read-only package files. are static, read-only files that you include inside your app’s package. Package files are installed once per machine and shared by all users. By default, Windows installs package files under the %ProgramFiles%WindowsAppsPackageFullName directory.[33] Your package’s binary files, WinRT components, and other asset files are all staged (installed) in this directory. When all users on the system uninstall this version of your package, this directory and its contents are permanently removed.

  • Read-write package files. are for per-user data created by your app (or background tasks) at runtime. Windows manages the per-user package files under the %UserProfile%AppDataLocalPackagesPackageFamilyName directory. This directory has several subdirectories to support local, roaming, and temporary package files that were discussed in Chapter 4. The system itself creates some additional subdirectories to manage system services (such as the background transfer service and Internet cache discussed in Chapter 7) on your package’s behalf. Your app can create whatever files it so desires under the LocalState, RoamingState, and TempState directories. The files are for your package and remain on the user’s machine when the user upgrades to newer versions of your package. The package files are permanently destroyed if the user uninstalls your package.

  • User files. are considered to be owned and managed by the user, not an app or a package. They typically contain documents, pictures, music, or videos. The user decides where these files are stored, but they usually reside in one of the user’s Documents, Pictures, Music, or Videos libraries. However, they can also reside on network shares or in cloud storage (such as SkyDrive). Your app can access user files but only with user consent. Users explicitly consent via a folder or file picker or implicitly consent by choosing to install your package after being notified that your package has specified a user file capability in its manifest. More details related to consent are presented later in this chapter. Because user files are managed by the user, the files can be accessed by multiple apps and can also be accessed by multiple users. Upgrading or uninstalling a package has no impact on user files. Table 5-1 summarizes the differences between the three different file classifications.

Table 5-1. File-classification differences.

Feature

Read-only package files

Read-write package files

User files

File accessibility

Read-only

Read/write

Read/write

Location decided by

Windows

Windows

User

Stored per-user

No

Yes

User decides

Accessible by other apps

No

No

Yes

Destroyed on package uninstall

Yes

Yes

No

Destroyed on package upgrade

Yes

No

No

App requires user consent to access

No

No

Picker/capability

Accessing read-only package files

When a user installs a package, the system unzips the entire contents of the package file into a %ProgramFiles%WindowsAppsPackageFullName directory. Of course, you can include any kinds of files you desire in the package. At runtime, your app has read-only access to the contents of this subdirectory.

Note

When debugging an app using Visual Studio, the package directory is under the project’s directory (usually in a bindebug subdirectory). This subdirectory is writable; however, your app should not write to this subdirectory because it will succeed during development and fail when properly installed (staged and registered).

There are two ways you can access a read-only package file: by storage folder or by URI. The following code shows how to obtain a StorageFolder object representing your package’s directory and then how to get a file in this directory:

// Get StorageFolder object that represents our package's install directory:
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
// In our package's directory go to the Assets subdirectory and find the Image.png file:
StorageFile file = await folder.GetFileAsync(@"AssetsImage.png");

Once you have a StorageFile object, you can open it and read its contents. I discuss how to do this in Chapter 6.

Alternatively, you can access the same file using a special URI:

StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(
   new Uri("ms-appx:///Assets/Image.png"));

This Microsoft-specific URI scheme (ms-appx) tells Windows you want to access a file that is included in your app’s package. Note the three slashes after the colon. The third slash indicates the omission of the package’s name. Therefore, “ms-appx:///Assets/Image.png” is equivalent to

"ms-appx://" + Windows.ApplicationModel.Package.Current.Id.Name + "/Assets/Image.png"

Using the URI technique is very useful because there are occasions when a URI is mandatory—for example, setting the source attribute of a XAML Image control. In addition, when you use a URI, you are tapping into the Windows resource system. This means that Windows will search directories looking for the file based on the user’s culture, contrast settings, and display DPI information.

Let’s briefly take DPI as an example. When you have a file Image.png, you can provide scaled versions of this asset for 100, 140, and 180% DPI, by tagging it with .scale followed by the percentage—for example, Image.scale-140.png. If you don’t provide these explicit versions, Windows will scale the 100% version up, which will inevitably lead to blurry or jagged images. By providing explicit files for each DPI setting, you can make sure that the image looks good on all devices. Similar schemes exist for contrast (for example, contrast-high), language (for example, lang-nl-NL), layout direction (for example, layoutdir-RTL), and so on. You can concatenate those resource qualifiers on the specific resources like this: Image.scale-140_contrast-high.png. See http://msdn.microsoft.com/en-us/library/windows/apps/xaml/Hh965324(v=win.10).aspx for more information.

Accessing read-write package files

When a user installs a package, the system creates a subdirectory where the app can create and manage some per-user files. The directory can be found here: %UserProfile%AppDataLocalackagesPackageFamilyName. Because the PackageFamilyName does not include a version number, this directory is used by all versions of a particular package.[34] This directory contains three subdirectories: LocalState, RoamingState, and TempState; these are described in Chapter 4. In these subdirectories, your app can create additional subdirectories, up to 32 levels deep if desired.

Important

The contents of the TempState subdirectory can be destroyed by the system at any time. By default, the system cleans it out once per week by using a scheduled task. A user can modify the frequency of this using the Windows Task Scheduler.[35] The scheduled task is called CleanupTemporaryState, and it can be found under Microsoft > Windows > ApplicationData.

There are two ways you can access per-user package files: by storage folder or by URI. To get a StorageFolder, you first get your package’s current ApplicationData object and then you query its LocalFolder, RoamingFolder, or TemporaryFolder property. You can use the URI technique (with a URI scheme of ms-appdata) to get a StorageFile object for a file that was previously created using the StorageFolder technique. The following code demonstrates how to create a file in each folder and subsequently get a reference to the file using the URI technique. The code also shows the various flags exposed by CreationCollisionOption to handle potential file-name conflicts.

StorageFolder folder;
StorageFile file;
// Create and then access a local package file:
folder = ApplicationData.Current.LocalFolder;
file = await folder.CreateFileAsync("LocalFile.txt",
   CreationCollisionOption.ReplaceExisting);
file = await StorageFile.GetFileFromApplicationUriAsync(
   new Uri("ms-appdata:///local/LocalFile.txt"));
// Create and then access a roaming package file:
folder = ApplicationData.Current.RoamingFolder;
file = await folder.CreateFileAsync("RoamingFile.txt",
   CreationCollisionOption.GenerateUniqueName);
file = await StorageFile.GetFileFromApplicationUriAsync(
   new Uri("ms-appdata:///roaming/RoamingFile.txt"));
// Create and then access a temporary package file:
folder = ApplicationData.Current.TemporaryFolder;
file = await folder.CreateFileAsync("TemporaryFile.txt",
   CreationCollisionOption.OpenIfExists);
file = await StorageFile.GetFileFromApplicationUriAsync(
   new Uri("ms-appdata:///temp/TemporaryFile.txt"));
// The line below deletes all read-write package files:
await ApplicationData.Current.ClearAsync();

In this chapter’s Performing file and folder queries section, we’ll show how to execute queries over files and folders. However, for this to work, Windows must know which folders contain files to index. Windows Search does not index the contents of your package’s folders; see http://msdn.microsoft.com/en-us/library/windows/desktop/bb266513.aspx for details. However, if you create a folder called “Indexed” in your package’s local folder, Windows Search will index the contents of this folder.[36] Searches the user performs using File Explorer will not show results that include the contents of the folder unless the File Explorer is positioned at the root of this folder: %UserProfile%AppDataLocalPackagesPackageFamilyNameLocalStateIndexed. Also, you can check whether the contents of this folder have been indexed by Windows Search by opening the Windows desktop Control Panel and then opening the Indexing Options dialog box. If it says “Indexing complete” at the top, you know the contents of this directory have all been indexed.

Of course, the reason to do this is so that your app can perform programmatic queries against the indexed files. The following code demonstrates how to create the “Indexed” folder, add some text files to it, and then perform a query against the folder:

// Create the local package folder's "Indexed" subdirectory:
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
StorageFolder indexedSubdir = await localFolder.CreateFolderAsync("Indexed");
// Create two text files in the "Indexed" subdirectory:
await FileIO.WriteTextAsync(await indexedSubdir.CreateFileAsync("File1.txt"), "abc");
await FileIO.WriteTextAsync(await indexedSubdir.CreateFileAsync("File2.txt"), "abcd");
// Create a query that looks for .txt files containing "abcd"
var qo = new QueryOptions(CommonFileQuery.DefaultQuery, new[] { ".txt" }) {
   ApplicationSearchFilter = "abcd",
     IndexerOption = IndexerOption.OnlyUseIndexer,   // Indexed files only!
   FolderDepth = FolderDepth.Deep   // Search subdirectories too
};
StorageFileQueryResult searchResults = localFolder.CreateFileQueryWithOptions(qo);
// Perform the query and get the results
IReadOnlyList<StorageFile> result = await searchResults.GetFilesAsync();
// 'result' contains a single StorageFile object referring to File2.txt

Accessing user files via explicit user consent

Users typically have files they care deeply about in various folders, such as the Documents, Pictures, Music, and Videos libraries, network shares, and so on. Windows desktop apps have always been able to access the user’s files and folders arbitrarily. This allows desktop apps to traverse the user’s folders and modify any files it finds there. Or, a desktop app might upload those files to a web server somewhere on the Internet. Clearly, this is not an ideal situation and it has caused users to be scared to use Windows desktop applications. To address users’ valid concerns, a Windows Store app can access only its own package files without user consent.

If your app wishes to manipulate a user file, your app can present the user with a folder or file picker. (See Figure 5-2.) The picker allows the user to navigate over her own folders and files securely. The user can then select a single folder, single file, or set of files, thereby granting your app access to the selected item or items only. In addition, the user can cancel the picker, thereby granting your app no access to anything. The key point here is that the user is in control of her files, not your app.

Note

In reality, the user is granting your package access to the folder or files. So your app can prompt the user for consent to allow a background task (that is part of the same package) access to the folder or files.

File-open picker in multiple-select mode with basket shown at bottom.

Figure 5-2. File-open picker in multiple-select mode with basket shown at bottom.

Let’s examine the UI for the file-open picker shown in Figure 5-2. This file-open picker dialog has a single-select mode and a multiple-select mode. Figure 5-2 shows the picker in multiple-select mode. As the user selects files from the UI, the files are placed in what’s called the basket, which appears at the bottom of the screen. As soon as any file is in the basket, the file-open picker enables the Open button at the lower-right corner. The user can remove files from the basket by tapping the items in the basket or deselecting items in the folder’s list. Some apps implement the FileOpenPicker, FileSavePicker, and CachedFileUpdater declarations in their manifest to provide the user a custom file open/save UI experience. For example, Microsoft’s SkyDrive Windows Store app does this, allowing users to download files from cloud storage. The resulting file is then returned back to the hosting Windows Store app. A hosted view (as discussed in Chapter 3) is not allowed to show a picker; attempting to do so throws an exception. The reason why Windows forces this limitation is because pickers are hosted themselves and it would be confusing to end users to have hosted views nest other hosted views.

The picker classes are defined in the Windows.Storage.Pickers namespace. Here is code prompting the user to select a folder via the FolderPicker:

var fop = new FolderPicker { FileTypeFilter = {"*"} };
var folder = await fop.PickSingleFolderAsync();
if (folder == null) return; // User canceled the picker
// folder refers to the user-selected StorageFolder object

There are three types of pickers: FolderPicker, FileOpenPicker, and FileSavePicker. Table 5-2 lists them with their properties.

Table 5-2. Pickers and their properties.

Property

FolderPicker

FileOpenPicker

FileSavePicker

CommitButtonText

String (example: “Choose this folder”)

String (example: “Choose this file”)

String (example: “Choose this file”)

SuggestedStartLocation (PickerLocationId)

Documents, Computer, Desktop, Downloads, HomeGroup, Music, Pictures, Videos

Documents, Computer, Desktop, Downloads, HomeGroup, Music, Pictures, Videos

Documents, Computer, Desktop, Downloads, HomeGroup, Music, Pictures, Videos

SettingsIdentifier

String (for separate user state: location and file type)

String (for separate user state: location and file type)

String (for separate user state: location and file type)

FileTypeFilter

“.jpg”, “.bmp”, and so on

“.jpg”, “.bmp”, and so on

ViewMode

List/thumbnail

List/thumbnail

DefaultFileExtension

“.jpg”

FileTypeChoices

“Images”: “.jpg”, “.png”

SuggestedFileName

“MyPicture”

SuggestedSaveFile

StorageFile

Method to Show

PickSingleFolderAsync

PickSingleFileAsync
PickMultipleFilesAsync
PickSaveFileAsync

For the folder and file-open picker, only the FileTypeFilter property is mandatory. This property is a collection of strings telling the picker which file types to show. Your app can use the wildcard (“*”) string to show all files to the user. All the other properties are self-explanatory except for one, SettingsIdentifier. Your app can set the SettingsIdentifier property to any string value. When your app shows a picker for the first time, the picker will navigate to the folder specified by the SuggestedStartLocation. However, the user can use the picker to navigate to a different folder. When the user subsequently picks a location or file and closes the picker, the system saves this last-selected location. If, in the future, your app brings up a picker with the same SettingsIdentifier value, the picker remembers the user’s last location and navigates to it directly, thereby overriding the SuggestedStartLocation. This is the reason why the name of the property is SuggestedStartLocation; it is a suggested location and not a demand.[37]

A FolderPicker returns a StorageFolder object representing a folder that the user is allowing your app to use. With this StorageFolder object, your app can access any files in this folder and any child items such as subfolders and files in any subfolders. This grants your app a lot of power; use it wisely. The folder picker UI does not give any indication to the user that your app gets access to the folder and its complete contents.

When the user selects a file via a picker, the system returns a StorageFile object, allowing your app access to the file. But the user could switch away from your app, causing the system to suspend it and possibly even terminate it. (See Chapter 3 for reasons why.) When the user switches back to your app, the system launches it again and your app is supposed to act as if it were running the whole time. However, when Windows terminated your app, the StorageFile object got destroyed and, now, your app can no longer access the user-selected file. Your app could present the user with another picker and have her grant consent to your app again, but this would be a horrible user experience. What we need is a mechanism that allows your package to remember the StorageFile and StorageFolder objects that the user granted your app’s package access to.

Fortunately, this mechanism does exist, and it’s called the FutureAccessList. Each package has a single FutureAccessList property exposed by the static type StorageApplicationPermissions, and it’s a simple dictionary of key/value pairs. The keys are Strings, and we call them tokens. The values contain an IStorageItem and a String, which we call metadata. The metadata string is optional; if you don’t specify it, the empty string (“”) is used. When adding an IStorageItem object to the FutureAccessList, you can specify a specific key string, but you don’t have to. If you omit a key (token), a GUID will be generated and used as the key.

The following line adds an entry to the FutureAccessList. The key is “FileWeWereUsing”, and the value is some StorageFile object as well as some metadata String:[38]

StorageApplicationPermissions.FutureAccessList.AddOrReplace(
   "FileWeWereUsing", storageFile, "SomeMetadata");

If your app terminates and launches again, you regain access to the file by doing this:

StorageFile file = await
StorageApplicationPermissions.FutureAccessList.GetFileAsync("FileWeWereUsing");

Getting the metadata string back (if you need it) is a bit trickier:

String metadata = StorageApplicationPermissions.FutureAccessList.Entries
   .First(e => e.Token == "FileWeWereUsing").Metadata;

The FutureAccessList can hold up to 1,000 storage items, and your code controls addition and removal. The StorageApplicationPermissions static class also offers a MostRecentlyUsedList property. The MostRecentlyUsedList works exactly like the FutureAccessList, except it holds up to 25 storage items and Windows manages them automatically for you. That is, when you add a new storage item and the list has reached its limit of 25, the oldest item is automatically removed. Moreover, the system sorts the items in the MostRecentlyUsedList; thus, if you enumerate the storage items, the items are returned in most-recently-used order. Finally, because both lists implement the IStorageItemAccessList interface, their APIs are identical. The interface looks like this:

public interface IStorageItemAccessList {
   UInt32 MaximumItemsAllowed { get; }       // Returns 1000 or 25
   void Clear();                             // Erases all entries in the collection
   String Add(IStorageItem file, string metadata);  // Returns token (GUID)
   void AddOrReplace(String token, IStorageItem file, String metadata);
   void Remove(String token);
   Boolean ContainsItem(String token);
   IAsyncOperation<IStorageItem>  GetItemAsync(String token);
   IAsyncOperation<StorageFolder> GetFolderAsync(String token);
   IAsyncOperation<StorageFile>   GetFileAsync(String token);
   AccessListEntryView Entries { get; }      // Returns collection of token/metadata pairs
   // Some members not shown here
}

One of the really cool features of these lists is that they automatically track changes to the storage item. Specifically, if the user uses another application (for example, File Explorer or cmd.exe) to rename or move the item to another subdirectory on the same disk volume, your app will still be able to access the item with its new name or location after extracting it from a list.[39]

Note

When your app accesses files or folders with the pickers or one of the IStorageItemAccessList classes, WinRT forwards the storage APIs your app calls to another process called a Broker (RuntimeBroker.exe). The broker process is a very important part of the Windows security model because it ensures that your app is allowed to access only the files and folders that the user consents to. Because the WinRT file APIs now have to make an additional cross-process call through the broker instead of going directly to the file system, you need to be aware that performance might suffer in order to provide users with the additional security of restricting apps’ access to the file system.

File-type associations

When a user double-clicks a text file in File Explorer or opens a text file from within an email application, typically Notepad.exe starts up and opens the document. This happens because Windows has associated Notepad.exe with the file extension .txt. Similarly, you can associate file extensions with your own Windows Store app. Your app can define its own file extension, or it can use an already-existing extension such as .txt. When a user opens a file, Windows checks to see how many apps have registered for the file’s file extension. If multiple apps have registered for the file extension, Windows presents the user with a list of these apps. The user can then decide which app to use and indicate if this app should be the default in the future. (See the dialog box shown in Figure 5-3.)

Dialog box prompting the user to choose the app for a given file extension.

Figure 5-3. Dialog box prompting the user to choose the app for a given file extension.

The user can also view and edit all the file-type associations in the system by selecting Settings charm > Change PC Settings > Search & Apps > Defaults > Choose Default Apps By File Type. This displays the pane shown in Figure 5-4.

The Choose Default Apps By File Type pane.

Figure 5-4. The Choose Default Apps By File Type pane.

For a Windows Store app, launching a file is similar to using a file-open picker because the user is trying to open a file and the user is in control of which app should be used to open the file. When an app is installed, Windows needs to know which file types your app supports so that it can activate your app when the user opens a supported file type. You declare file-type associations for each file type you want your app to support in your package’s manifest. (See Figure 5-5.)

Declaring a file-type association in an app’s manifest.

Figure 5-5. Declaring a file-type association in an app’s manifest.

Note

When declaring support for file-type associations, there are many file types that are forbidden, including .accountpicture-ms, .appx, .application, .appref-ms, .bat, .cer, .chm, .cmd, .com, .cpl, .crt, .dll, .drv, .exe, .fon, .gadget, .hlp, .hta, .inf, .ins, .jse, .lnk, .msi, .msp, .ocx, .pif, .ps1, .reg, .scf, .scr, .shb, .shs, .sys, .ttf, .url, .vbe, .vbs, .ws, .wsc, .wsf, and .wsh. The most current list can be found at http://msdn.microsoft.com/en-us/library/windows/apps/hh779669.aspx.

Table 5-3 describes the various properties.

Once you’ve built your package and deployed it on a machine, Windows will know about its file-type associations. You can verify this using File Explorer to see your manifest values appear as in Figure 5-6.

Table 5-3. File-type association properties and their descriptions.

Property

Required

Description

Name

Unique name (lowercase) for all associations (Content type/File type) sharing

DisplayName, Logo, Info Tip, and Edit Flags

Display name

String shown in File Explorer’s Type column

Logo

Icon shown in File Explorer (example: “AssetsIcon.png”)

You should define four icons named like this: Icon.targetsize-[16 | 32 | 48 | 256].png

File Explorer picks the target size based on List, Medium, Large, and Extra Large views

Info tip

Tooltip text when hovering in File Explorer

Edit flags

Open is safe: indicates the file can do no harm to the system (.txt file; not an .exe file).

Always unsafe: indicates that the file type should never be trusted (.exe file).

You should select neither or one of these options; do not select both.

For more info, look up the Win32 FILETYPEATTRIBUTEFLAGS enum type.

Content type

Mime type (example: “Image/jpeg”)

File type

File extension (example: “.jpg”)

File Explorer showing file-type information for a .jeff file obtained from the app’s manifest.

Figure 5-6. File Explorer showing file-type information for a .jeff file obtained from the app’s manifest.

When the user launches your Windows Store app via a file-type association, Windows calls your app’s OnFileActivated override instead of your app’s OnLaunched method. In this OnFileActivated method, Windows passes a FileActivatedEventArgs object:

public sealed class FileActivatedEventArgs : IActivatedEventArgs,
IApplicationViewActivatedEventArgs {
   // Members specific to FileActivatedEventArgs objects:
   public String Verb { get; }
   public IReadOnlyList<IStorageItem> Files { get; }
   public StorageFileQueryResult NeighboringFilesQuery { get; }
   // IActivatedEventArgs members:
   public ActivationKind Kind { get; }
   public ApplicationExecutionState PreviousExecutionState { get; }
   public SplashScreen SplashScreen { get; }
   // IApplicationViewActivatedEventArgs member:
   public Int32 CurrentlyShownApplicationViewId { get; }
}

The Verb property enables your app to handle different operations on the file, such as Open and Edit. The Files property contains the set of files the user selected when she launched your app. The following code shows the verb and the first file the system passes to your app:

protected override async void OnFileActivated(FileActivatedEventArgs args) {
   IStorageItem firstFile = args.Files[0];
   await new MessageDialog(
      String.Format("Activated to '{0}' the '{1}' file.

Path='{2}'",
         args.Verb, firstFile.Name, firstFile.Path)).ShowAsync();
}

Sometimes, when launching a file, the launched app would like to process the neighboring files too. For example, the Windows Mail app allows the user to tap on a picture attachment, which launches a photo viewer app. If the mail message has multiple photos in it, the user could go back to the message and tap on each photo attachment individually to look at them all. But this is rather inconvenient. It would be better if the user could tap on one of the photo attachments, launch a photo viewer app, and then navigate through all the attachment’s photos. To enable this kind of scenario, the Mail app would first download all of a message’s attachments into a folder and then launch one of the files, thereby activating the photo viewer app. The photo viewer app would query FileActivatedEventArgs’s NeighboringFilesQuery property. If this property is not null, it refers to a StorageFileQueryResult object that is scoped to the folder containing the file that launched it. The photo viewer app can now call StorageFileQueryResult’s GetFilesAsync method to access the other files in the same folder.

A StorageFileQueryResult object is scoped to whatever launched it. For example, if the user launches a file from her desktop, the StorageFileQueryResult object is scoped to the user’s desktop. If the user was in File Explorer and did some complex search query and then launched a file, the StorageFileQueryResult object is scoped to File Explorer’s search results. Furthermore, the StorageFileQueryResult object is scoped to the same kinds of files, which must be pictures, music, or videos. That is, if the user launches a .jpg file, the StorageFileQueryResult object will return only other image files (like .gif, .png, .tif, and so on). Your app does not need to declare file-type associations for all these file types. One last thing, if the user launches an app, passing it multiple storage items, then FileActivatedEventArgs’s NeighboringFilesQuery property will always return null.

So far, we’ve been talking about how your app gets activated because of a file-type association. Now, we’ll discuss how an app can activate another app’s file-type association. Here is code allowing the user to select a file and then open the file by launching its corresponding app:

StorageFile file = new FileOpenPicker { FileTypeFilter = { ".txt" } }.PickSingleFileAsync();
Boolean launched = await Launcher.LaunchFileAsync(file);

Note

For Windows desktop apps, the ability to launch a file has created many security-related issues for Windows. For example, mail attachments that run .exe files can install viruses or do other harm to the user’s PC. To greatly improve the security of a user’s PC, Windows Store apps are greatly restricted as to what files they can launch. For example, Launcher’s methods prevent launching a desktop app (which runs less restricted than a Windows Store app) with files that could execute code, such as .asp, .aspx, .bat, .cmd, .com, .dll, .exe, .inf, .jar, .js, .mdb, ,msi, .pl, .vb, .vbs, .wsf, .vsi, and so forth.

In addition, you’ll notice that LaunchFileAsync returns a Boolean (true if the launch is successful, and false if the launch failed). If the launch fails, the system exposes no way to find out the reason why. No additional information is given because Microsoft doesn’t want malicious apps to learn more about the failure in order to attempt to work around it. Also, calling LaunchFileAsync throws an exception if it’s not called from a UI thread or if it’s called from a UI thread whose window is not active. This prevents apps from popping up and activating themselves at arbitrary times, disturbing the user’s workflow, and from capturing user input (like passwords). And finally, there is an overload of the LaunchFileAsync method that accepts a LauncherOptions object. Your app can create one of these and set options that force the user to select the app to be launched for a certain file type or always display a warning to the user that the file being launched is potentially unsafe.

It’s also possible to launch an app to access a file via a URI. This is useful for apps that can access files directly from an Internet location. This feature is called direct invoke, and it requires that you associate a content type with your file-type association. For more about content types, see the IANA website here: http://www.iana.org/assignments/media-types. Because our file-type association declared a content type of “application/jeff”, another app can launch our app via a URI as follows:

Uri uri = new Uri("http://Wintellect.com/SomeFile");    // URI to file on Internet
LauncherOptions options = new LauncherOptions { ContentType = "application/jeff" };
Boolean ok = await Launcher.LaunchUriAsync(uri, options);

Launching an app this way also causes its OnFileActivated method to be called; the URI can be found in the StorageFile’s FolderRelativeId property.

Developers frequently ask how their Windows Store app can launch another app. Because of security concerns, there is no direct way for one app to launch another. However, Windows Store apps can be activated if they declare file-type associations (as just discussed) or if they declare URI protocols. Both of these techniques are indirect; that is, an app launches a file or a URI protocol, but the user controls which app actually starts running in response to this. The user views and edits all the URI protocol associations in the system by selecting Settings charm > Change PC Settings > Search & Apps > Defaults > Default Apps By Protocol. There is no way for a Windows Store app to directly launch another app. Again, this is by design for security reasons.

Like file-type associations, URI protocols are declared via the app’s manifest and then the app should override Windows.UI.Xaml.Application’s OnActivated method to handle the activation.

For example, the Bing maps app has declared support for the “bingmaps” URI protocol, and this allows another app to launch the Bing maps app with the following code:

// See http://msdn.microsoft.com/en-us/library/windows/apps/jj635237.aspx for Maps URI scheme
var uri = new Uri("bingmaps:?where=1600%20Pennsylvania%20Ave,%20Washington,%20DC");
await Launcher.LaunchUriAsync(uri);

Storage item properties

The IStorageItem interface offers properties applicable to both files and folders. These properties are Name, Path, DateCreated, and Attributes (Normal, ReadOnly, Directory, Archive, Temporary, and LocallyIncomplete[40]). In addition, both the StorageFile and StorageFolder classes implement the IStorageItemProperties interface, which defines the DisplayName, DisplayType, and FolderRelativeId properties. And the IStorageFile interface offers some file-specific properties: FileType and ContentType. Table 5-4 shows all these properties and an example of what they look like.

Table 5-4. Various properties available on a StorageFile object.

Interface/class

Property

Example value

IStorageItem

Name

“photo.JPG”

Path

“E:Pictures2013photo.JPG”

DateCreated

{5/22/2013 11:59:00 AM -07:00}

Attributes

FileAttributes.Archive

IStorageItemProperties

DisplayName

“photo”

DisplayType

“JPEG image”

FolderRelativeId

“5EF38814DAD2922E\photo.JPG”

StorageFile

FileType

“.JPG”

ContentType

“image/jpeg”

The IStorageItem interface also defines a GetBasicPropertiesAsync method that ultimately returns a BasicProperties object exposing some other properties common to both files and olders:

public sealed class BasicProperties : IStorageItemExtraProperties {
   // Gets the timestamp of the last time the file was modified.
   public DateTimeOffset DateModified { get; }
   // Gets the most relevant date for the item.
   // For a photo, date taken. For a song, date released.
   public DateTimeOffset ItemDate { get; }
   // Gets the size of the file.
   public UInt64 Size { get; }
}

You can also think of a thumbnail image as being a property, and your app can obtain this property by querying IStorageItemProperties’ GetThumbnailAsync method. However, this method should no longer be used; instead, both StorageFile and StorageFolder implement the new IStorageItemProperties2 interface, which defines a GetScaledImageAsThumbnailAsync method. This method can return thumbnail images of any size or cropped to meet your app’s needs. The method scans for the thumbnail you desire from the PC’s local cache first. If the thumbnail is not found, the method checks the file itself for an embedded thumbnail. Finally, if the file is available only in the user’s SkyDrive, the method makes a request to the SkyDrive service to have the service produce and download a thumbnail image. The great thing here is that the file itself (which could be huge in the case of a 20-MB image) does not get downloaded in order to get and show the user a thumbnail for it; this reduces bandwidth usage, reduces local disk consumption, and improves performance. Of course, if the SkyDrive service can’t be reached, GetScaledImageAsThumbnailAsync returns null.

However, we have just discussed the very tip of the property-system iceberg. The Windows property system is enormous and incredibly rich. Windows actually captures many properties related to specific data files and stores them in a database on your hard disk. This database is called the content indexer, and I discuss some other features it offers in the Searching over a stream’s content section in Chapter 6. This allows your app to query (and modify) a phenomenal set of properties. The IStorageItemProperties interface offers a Properties property that returns a StorageItemContentProperties object. The class looks like this:

public sealed class StorageItemContentProperties : IStorageItemExtraProperties {
   public IAsyncOperation<IDictionary<String, Object>> RetrievePropertiesAsync(
      IEnumerable<String> propertiesToRetrieve);
   public IAsyncAction SavePropertiesAsync();
   public IAsyncAction SavePropertiesAsync(
      IEnumerable<KeyValuePair<String, Object>> propertiesToSave);
   // Convenience methods that internally call RetrievePropertiesAsync to
   // get commonly used properties for commonly used file types
   public IAsyncOperation<DocumentProperties> GetDocumentPropertiesAsync();
   public IAsyncOperation<ImageProperties> GetImagePropertiesAsync();
   public IAsyncOperation<MusicProperties> GetMusicPropertiesAsync();
   public IAsyncOperation<VideoProperties> GetVideoPropertiesAsync();
}

When calling RetrievePropertiesAsync, you must pass it a collection of strings identifying the properties you wish to obtain. Go to http://msdn.microsoft.com/en-us/library/dd561977(VS.85).aspx to see the enormous list of all possible strings. To get strings for some common properties, see the static Windows.Storage.SystemProperties class. The following code shows an example calling this method:

IDictionary<String, Object> props = await storageFile.Properties.RetrievePropertiesAsync(
   new String[] {
      "System.FileAttributes", "System.DateModified",
      "System.Size", SystemProperties.ItemNameDisplay
   });

You can retrieve literally hundreds of predefined properties, ranging from the straightforward FileOwner to the more esoteric System.ComputerName or even System.FreeSpace (free space on the system disk).

To simplify your code when obtaining commonly used properties for common file types, the StorageItemContentProperties class offers the four GetXxxPropertiesAsync methods to easily obtain properties commonly used when apps work with documents, images, music, or videos. Table 5-5 shows the properties returned for a specific file type.

Table 5-5. Commonly used properties for document, image, music, and video files.

File type

Properties

Document

Author, Comments, Keywords, Title

Image

CameraManufacturer, CameraModel, DateTaken, Height, Keywords, Latitude, Longitude, Orientation, PeopleNames, Rating, Title, Width

Music

Album, AlbumArtist, Artist, Bitrate, Composers, Conductors, Duration, Genre, Producers, Publisher, Rating, Subtitle, Title, TrackNumber, Writers, Year

Video

Bitrate, Directors, Duration, Height, Keywords, Latitude, Longitude, Orientation, Producers, Publisher, Rating, Subtitle, Title, Width, Writers, Year

Accessing user files with implicit user consent

So far, we’ve discussed accessing user data through file pickers and file-type associations. In both scenarios, the end user explicitly gives your app access to a file or folder. But what about a photo viewer app that allows the user to browse all his pictures? Or a music player that shows the user’s songs organized by artists or albums? Or an app that indexes all the user’s documents, allowing him to search through them? Your app could certainly use pickers for these scenarios. However, that would force the user to choose the folders containing his pictures or music files before he could use the app. Moreover, the user already has a dedicated virtual location for media files and documents in libraries that we could use. (See the side note if you’re unfamiliar with libraries.)

Windows Store apps are forbidden from accessing a user’s files unless the user allows the app to do it. For an app to traverse the contents of a library, the app’s package manifest must first specify in the Capabilities section which libraries the package wants access to. Figure 5-7 shows the capabilities related to accessing a user’s files. Selecting a library capability in the manifest grants your package bulk access to a large set of the user’s files. Your package’s app must be diligent here and not abuse this privilege. In fact, packages that use library capabilities are scrutinized much more stringently than packages that use pickers when submitted for Windows Store certification. If you can implement your app using pickers instead of library capabilities, you should. You should specify library capabilities only when your package absolutely requires programmatic access to the user’s files.

Capabilities in the manifest file to access libraries.

Figure 5-7. Capabilities in the manifest file to access libraries.

Important

Whereas the Pictures library contains pictures, the Music library contains music, and the Videos library contains videos, the Documents library contains all kinds of files on behalf of the user. My personal Documents library contains Microsoft Excel files, PowerPoint files, Word files, PDF files, Microsoft Money files, C# source code files, and the list goes on and on. Having access to all these files opens up security issues where private user files could too easily be accessed and even uploaded to servers somewhere on the Internet. For this reason, packages are strongly discouraged from enabling the Documents Library capability in the manifest. In fact, as you can see in Figure 5-7, Visual Studio doesn’t even show “Documents Library” in the list of Capabilities. If you really want to use this capability, you must manually add it to the manifest’s XML file.

In addition, packages that specify the Documents Library capability will not pass Windows Store certification if submitted using an individual account. Only company accounts (which are verified) can submit packages that specify the Documents Library capability. Furthermore, a package that specifies the Documents Library capability must also specify one or more file-type associations. This gives the app access to only the specified file types within the library. From a security perspective, having apps declare a file-type association is the equivalent of having a virtual documents library like “My Excel Files,” which the user can feel more comfortable granting access to.

Then, when the user goes to the Store app to install the package, the Store app shows the user what capabilities the package requires. By installing the package, the user is implicitly granting the package access to the specified capabilities. Figure 5-8 shows how the Store app shows a package’s required capabilities to a user before the user installs the package.

The Music app requires access to the user’s Music library.

Figure 5-8. The Music app requires access to the user’s Music library.

After a user installs a package, she can always see what capabilities the package needs by running the package’s app, opening the Settings charm, and then selecting Permissions, as you can see in Figure 5-9.

The Music app has permission to access your Music library and to the Internet.

Figure 5-9. The Music app has permission to access your Music library and to the Internet.

If a package specifies the Music Library capability, its app can easily access all the folders in this library with a single line of code:

IReadOnlyList<StorageFolder> folders = await KnownFolders.MusicLibrary.GetFoldersAsync();

If your package does not have the required capability, the system throws an “access denied” exception.

In the line just shown, you’ll notice that we use the KnownFolders class. This class exposes several StorageFolder objects:

public static class KnownFolders {
   public static StorageFolder CameraRoll       { get; }  // For Windows Phone only
   // The main library folders:
   public static StorageFolder PicturesLibrary  { get; }  // User's Pictures library
   public static StorageFolder SavedPictures    { get; }  // = PicturesLibrary (for Phone)
   public static StorageFolder MusicLibrary     { get; }  // User's Music library
   public static StorageFolder Playlists        { get; }  // Music library's play list folder
   public static StorageFolder VideosLibrary    { get; }  // User's Video library
   public static StorageFolder DocumentsLibrary { get; }  // User's Documents library (avoid)
   // Allows Picture, Music, Video library access on user's Home Group
   public static StorageFolder HomeGroup        { get; }
   // Allows Picture, Music, Video library access on removable devices:
   public static StorageFolder RemovableDevices { get; }
   // The folder of media server (Digital Living Network Alliance [DLNA]) devices.
   public static StorageFolder MediaServerDevices { get; }
}

Windows has a feature called HomeGroup that grants a user easy access to multiple machines in a home environment. Figure 5-10 shows under my name that I have two machines in my HomeGroup: BOSBOX8 and VIRTBOS.

Libraries available on multiple machines in a HomeGroup.

Figure 5-10. Libraries available on multiple machines in a HomeGroup.

You’ve seen that libraries can include physical directories on other systems to get a consolidated view of media files or documents. But what if a user wants to browse, for example, his wife’s pictures either on the home system or even on her computer? That is what a HomeGroup is for. The KnownFolders class exposes this too as a virtual folder. When a user first joins a machine to a HomeGroup, the system asks if the machine’s library should be shared. Your app can access these shared HomeGroup libraries by using KnownFolders’s HomeGroup property.

A package specifying the HomeGroup capability must also specify one or more of the Pictures Library, Music Library, or Videos Library capabilities. The package will also need the Home Or Work Networking capability, which is not on by default. (See Chapter 7.) For security reasons, it is not possible to get access to a HomeGroup machine’s Documents library.

The following code shows how to enumerate the contents of the virtual HomeGroup folder:

StorageFolder folder = KnownFolders.HomeGroup;
foreach (var user in await folder.GetFoldersAsync()) {            // Users
   foreach (var machine in await user.GetFoldersAsync()) {        // Machines
      foreach (var library in await machine.GetFoldersAsync()) {  // Libraries
         // Process a library folder...
      }
   }
}

The KnownFolders class also has a MediaServerDevices property. Like the virtual HomeGroup storage folder, use of this property also requires that one or more of the media library capabilities be specified as well as the Home Or Work Networking capability.

The KnownFolders.RemovableDevices property is used for removable storage such as USB drives. Accessing the contents on removable media has similar security concerns to that of accessing the contents of the Documents library. That is, the app must also specify at least one file-type association as well as the package’s Removable Storage capability. Just as when you use the Documents library, the system exposes only files on the removable storage device that meet the specified file-type association or associations. The following code shows how to enumerate the contents of the virtual removable devices storage folder:

StorageFolder devices = KnownFolders.RemovableDevices;
foreach (var device in await devices.GetFoldersAsync()) { // Drives
   foreach (var folder in await device.GetItemsAsync()) { // Files or folders
      //  Process a storage item...
   }
}

Windows Store apps can access files on the network through UNC paths too, for example:

StorageFile file = StorageFile.GetFileFromPathAsync(@"\SomeMachineSomeShareSomeFile.txt");

Windows treats shares on the network the same way as it treats KnownFolders.RemovableDevices; you will need to specify file-type associations to indicate the file types your app works with. Instead of specifying the Removable Storage capability in the manifest, the package must specify the Private Networks and Enterprise Authentication capabilities. The Enterprise Authentication capability allows the app to use the user’s credentials to authenticate on the remote system. Be aware that Enterprise Authentication is a special-use capability requiring that the package be submitted by a company account instead of an individual account.

One final folder to discuss is the user’s Downloads folder. On a PC, each user gets his or her own Downloads folder. When an app first puts something in the user’s Downloads folder, Windows creates a subfolder within the Downloads folder for the app. This subfolder name matches the app’s package family name, followed by an exclamation mark and the application ID (usually “App” as specified in the manifest’s Application element). This keeps one app’s downloaded files separate from another app’s downloaded files. Figure 5-11 shows this subfolder name as viewed from cmd.exe.

The app’s Downloads subfolder and its Desktop.ini file as viewed with cmd.exe.

Figure 5-11. The app’s Downloads subfolder and its Desktop.ini file as viewed with cmd.exe.

In the app’s subfolder, Windows also creates a hidden (and system) Desktop.ini file whose contents are shown in Figure 5-11. The existence of this file causes Windows File Explorer to show the subfolder with a different name (as indicated by the LocalizedResourceName value). So, when the user looks at his Downloads folder with File Explorer, he sees what’s shown in Figure 5-12.

The Downloads folder shows a subfolder matching the app’s name due to the Desktop.ini file placed in the subfolder.

Figure 5-12. The Downloads folder shows a subfolder matching the app’s name due to the Desktop.ini file placed in the subfolder.

Note that Downloads subfolders are not destroyed when packages are uninstalled because the files in these subfolders are considered to be user files, not package or app files. An app creates files and subfolders in its Downloads subfolder by calling methods defined by the DownloadsFolder class:

public static class DownloadsFolder {
   public static IAsyncOperation<StorageFile> CreateFileAsync(String desiredName);
   public static IAsyncOperation<StorageFile> CreateFileAsync(String desiredName,
      CreationCollisionOption option);
   public static IAsyncOperation<StorageFolder> CreateFolderAsync(String desiredName);
   public static IAsyncOperation<StorageFolder> CreateFolderAsync(String desiredName,
      CreationCollisionOption option);
}

The following code creates a text file for the user in the app’s Downloads subfolder:

IStorageFile file = await DownloadsFolder.CreateFileAsync("file.txt");
await FileIO.AppendLinesAsync(file, new [] { "Hello there"});

WinRT offers no API to query the contents of an app’s Downloads folder; therefore, your app must keep track of the folders and files it creates using the FutureAccessList property discussed earlier in this chapter.

Table 5-6 summarizes the capability and file-type association requirements of the various virtual storage folders.

Table 5-6. Capability and file-type association requirements for virtual storage folders.

Virtual folder

Requires capability

Requires file type assoc.

Notes

Music/Pictures/Videos Library

1+ required for HomeGroup.

Playlists

Requires Music Library capability.

HomeGroup

(special)

Requires Music, Pictures, or Videos Library capability.

DocumentsLibrary

HomeGroup can’t access this.

RemovableStorage

Children are drives.

MediaServerDevices

(special)

Children are media servers.

DownloadsFolder

Creates subfolders and files only.

Performing file and folder queries

This chapter has discussed how to work with folders, files, properties, thumbnail images, and libraries. In this section, I show how to quickly search for items within a folder using a query that can filter on multiple properties, such as date, size, rating, or even user-defined strings. You can also specify how you want the query results sorted. Additionally, you can receive notifications when storage items are added or removed.

The following code demonstrates how to perform a query over the user’s Pictures library. The query returns a set of virtual folders, with each folder representing a year.[42] Then, within each folder, pictures taken that year are returned. Let me make something perfectly clear: this works regardless of how the user organizes his pictures in his Pictures library. That is, all the pictures could be at the root of the user’s Pictures library, or they could be in subdirectories organized by person or location. None of this matters; the following code returns the pictures grouped by year:

// Create QueryOptions to filter/sort results; this example groups the results by year
QueryOptions qo = new QueryOptions(CommonFolderQuery.GroupByYear)
   { FolderDepth = FolderDepth.Deep };
// From the user's Pictures library, create a query that returns virtual folders
StorageFolderQueryResult folders = KnownFolders.PicturesLibrary
   .CreateFolderQueryWithOptions(qo);
// Process each year's files
foreach (StorageFolder folder in await folders.GetFoldersAsync()) {
   Debug.WriteLine(folder.Name);  // Folder name is year, e.g. "2014"
   foreach (StorageFile file in await folder.GetFilesAsync()) {
      Debug.WriteLine("   " + file.Name);  //  Pictures taken in 2014
   }
}

Wow, this is all there is to it. Let’s talk a little more about the QueryOptions class. You create and initialize an instance of this class to fully describe the kind of query you wish to perform. Table 5-7 summarizes the various options.

Table 5-7. QueryOptions constructor parameter and other property options.

Kind

QueryOptions member

Description

Constructor flags

CommonFolderQuery

DefaultQuery or GroupByType/Author/Tag/Year/Month

Artist/Album(Artist)/Composer/Genre/PublishedYear/Rating

CommonFileQuery

DefaultQuery or OrderByName/Title/SearchRank/Date/MusicInfo

Configurable properties

FileTypeFilter

File extension list (empty for all).

FolderDepth

Shallow (folder only) or Deep (folder and subfolders)

Language

Language ID string (example: “en-US”)

IndexerOption

UseIndexerWhenAvailable, OnlyUseIndexer, DoNotUseIndexer

ApplicationSearchFilter
UserSearchFilter

Advanced Query Syntax (AQS) strings combined together.

See http://msdn.microsoft.com/en-us/library/windows/desktop/bb266512.aspx.

SortOrder

Set of PropertyName/AscendingOrder pairs

See http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.search.queryoptions.aspx#properties.

Read-only properties

DateStackOption

Indicates how results are grouped (None, Month, or Year).

GroupPropertyName

Indicates the property being used to group the CommonFolderQuery.

When you construct a QueryOptions object, you pass to its constructor a CommonFolderQuery enumeration value or a CommonFileQuery enumeration value indicating whether you want results grouped by folder or an ordered flat set of results. The remaining properties are pretty self-explanatory; you can look them up in the SDK documentation if you need more information about them. When filtering on storage item properties, you have access to the complete set of properties as described in the Storage item properties section in this chapter. And you can create rich filter strings using the Windows Advanced Query Syntax (AQS) along with the ApplicationSearchFilter and UserSearchFilter properties.

For example, the following code creates a query that returns music files in the rock genre that are older than November 5, 2004 and whose album title contains the word “Sky”:

QueryOptions qo = new QueryOptions(CommonFolderQuery.GroupByPublishedYear) {
   FolderDepth = FolderDepth.Deep,
   ApplicationSearchFilter = "date:>11/05/04 AND genre:rock AND System.Music.AlbumTitle:~~Sky"
};
StorageFolderQueryResult folders = KnownFolders.MusicLibrary.CreateFolderQueryWithOptions(qo);

By the way, your app doesn’t have to process all of a query’s results. The StorageFolderQueryResult class offers an overload of the GetFoldersAsync method that takes a starting index and a max number of items. Similarly, the StorageFileQueryResult class offers an overload of the GetFilesAsync method that also takes a starting index and a max number of items.

Here is another example that creates a query resulting in a flat set of pictures ordered by date taken:

QueryOptions qo = new QueryOptions(CommonFileQuery.OrderByDate, new[] { "*" });
StorageFileQueryResult files = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(qo);
files.OptionsChanged += OnOptionsChanged;
files.ContentsChanged += OnContentsChanged;

This creates a flat, ordered list of all files by using a wild card (“*”). In addition, the OptionsChanged event handler will invoke our OnOptionsChanged method whenever one of our QueryOptions object’s properties gets changed. Also, the ContentsChanged event handler will invoke our OnContentsChanged method if any storage items change that affect the results of our query. An app can dynamically update its user interface in response to this event.

It is common for apps to process the resulting folders and files, getting thumbnail images and properties for each storage item. However, iterating over all the items individually to get this data would be quite time consuming, and it is also highly unlikely that all the information could fit on the user’s screen anyway. To acquire thumbnail images more efficiently, call QueryOptions' SetThumbnailPrefetch method. This causes the system to start loading thumbnail images immediately; this approach uses more resources but makes thumbnail retrieval on query results much faster. The QueryOptions class also offers a SetPropertyPrefetch method to get storage item properties more efficiently.

The following code demonstrates using these methods to improve the performance of fetching properties and thumbnail images:

// Create QueryOptions to filter/sort results
QueryOptions qo = new QueryOptions(CommonFileQuery.OrderByDate, new[] { "*" });
// Improve performance of fetching properties and/or thumbnails
String[] propertiesToRetrieve = new String[] { "System.Size" };
qo.SetPropertyPrefetch(PropertyPrefetchOptions.ImageProperties, propertiesToRetrieve);
qo.SetThumbnailPrefetch(ThumbnailMode.PicturesView, 190, ThumbnailOptions.None);
// From virtual folder call Create[File|Folder|Item]QueryWithOptions
StorageFileQueryResult files = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(qo);


[33] The %ProgramFiles%WindowsApps directory is a hidden, system directory, so you will not be able to see it in File Explorer unless you choose to show hidden items. Once you’ve found the directory and subsequently try to open it, you’ll get an “access denied” error. However, you can open any of this directory’s subdirectories. Windows actually determines the package directory via the PackageRoot value, which can be found in the registry in the HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionAppx key.

[34] For details about how to handle schema changes in your package’s data on upgrade, refer back to Chapter 4.

[35] To update it, you must export it to an XML file, edit the XML file, and then import it back in.

[36] The subdirectory must be named “Indexed”; this will not work if you use any other name.

[37] Windows persists SettingsIdentifier info in the registry here: HKEY_CLASSES_ROOTLocal SettingsSoftwareMicrosoftWindowsCurrentVersionAppModelSystemAppDataPackageFamilyNamePersistedPickerData. It is really a package-specific value; not an app-specific value.

[38] Windows persists access list info in the registry here: HKEY_CLASSES_ROOTLocal SettingsSoftwareMicrosoftWindowsCurrentVersionAppModelSystemAppDataPackageFamilyNamePersistedStorageItemTable.

[39] For the curious, Windows assigns a unique file Id to all files on an NTFS volume and this is how it can track these changes. For more information about this, check out the Win32 OpenFileById function and its FILE_ID_DESCRIPTOR parameter on MSDN.

[40] Other classic attributes like Hidden, System, Device, SparseFile, ReparsePoint, Compressed, Offline, NotContentIndexed, Encrypted, IntegrityStream, and NoScrubData are not exposed.

[41] Libraries aren’t actual physical folders, and when you programmatically browse the file system, you will not find them. They are stored as XML files in %UserProfile%AppDataRoamingMicrosoftWindowsLibraries.

[42] You can easily tell that the folders do not physically exist by looking at each StorageFolder’s Path property. For virtual folders, the property will show the empty string (“”) because there is no actual path to a disk location that can be shown.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.111.134