Simple async file access using Windows 8

This article refers to pre-release software. Some or all of what is presented could change between now and release.

This week at the Build Windows conference, Microsoft announced Windows 8 and introduced the new Windows Runtime APIs for the developers.

As a C# developer, one of the defining features of the Runtime is that a lot of the .NET Framework is “gone”, or moved to new namespaces. Nowhere is this more true than in System.IO, which is now fairly barren. The reason is that most of the file I/O APIs in the Windows Runtime are designed to be asynchronous, and as these new APIs are in the Runtime, they’ve moved into the Windows.* namespace.

As it turns out, reading and writing files using the new asynchronous APIs is a little convoluted. OK, its a lot convoluted, but I’ve put together some code that should simplify it for you, while also taking you through the steps involved.

When I think of reading or writing a file synchronously, I think of moving the reading or writing operation into a delegate that will be called for me when the time comes. For example, I would want to read a file asynchronously from our Documents library using code like this:

FileAsync.Read(KnownFolders.DocumentsLibrary, "myfile.txt", (fileSize, reader) =>
    {
        string text = reader.ReadString(fileSize);
 
        this.EditBox.Text = text;
    });

Let’s see what is needed to make this work.

First let’s pull in the namespaces we’ll need:

using System;
using Windows.Storage;
using Windows.Storage.Streams;

Now let’s create the shell of our class…

namespace SteveSaxon.IO
{
    public static class FileAsync
    {
    }
}

Next we’ll add support for reading files. This may seem overly complicated, but the await keyword is greatly simplifying it for us. In a nutshell, await allows you to make a call on a background thread, such that once it completes, the result of the call is dispatched back to the thread you made the call from. You get to write fairly natural looking code knowing that you’re never blocking the calling thread, but getting to take full advantage of all of the computing power in your system.

Eric Lippert posted a great article on how the await and async keywords work.

Better yet, if your asynchronous call ends up throwing an exception, this exception gets thrown in your calling thread too, so the exception case is also extremely elegant.

Eric also posted an article on how the exception work with await blocks.

Here is the code for our Read method:

public static async void Read(StorageFolder folder, string fileName,
    Action<uint, DataReader> reader)
{
    StorageFile file;
    IRandomAccessStream stream;
    IInputStream inputStream;
    DataReader rdr;
 
    file = await folder.GetFileAsync(fileName);
 
    stream = await file.OpenAsync(FileAccessMode.Read);
    inputStream = stream.GetInputStreamAt(0);
 
    uint fileSize = (uint)stream.Size;
 
    rdr = new DataReader(inputStream);
    rdr.LoadAsync(fileSize);
 
    reader(fileSize, rdr);
}

The StorageFolder first parameter may seem confusing, but it is the return type for the constants in the KnownFolders class, such KnownFolders.DocumentsLibrary and KnownFolders.PicturesLibrary.

One thing you may have noticed in the sample I presented: I was setting the text of an control in the UI based on the data I read in. In order for that to work, our delegate must have been called on the main thread, and in fact that is exactly what happens when you use await. When you issue an await, the awaited code is called on another thread, then joined back to the main thread when it completes.

If we had not been on the main thread, we would have needed to use code like this to marshal it back to the UI thread:

this.Dispatcher.InvokeAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
            (dummy, args) => this.EditBox.Text = text, this, null);

Now you can see how elegant the await keyword is!

To simplify using the code with any folder on our disk let’s add the following helper function:

public static async void Read(string folderPath, string fileName,
        Action<uint, DataReader> reader)
{
    StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
 
    Read(folder, fileName, reader);
}

Writing is even more steps than reading, but we’ll try to make it simple. I’ve included the folder helper here too:

public static async void Write(StorageFolder folder, string fileName,
        CreationCollisionOption collisionOption, Action<DataWriter> writer,
        Action<bool> complete = null)
{
    StorageFile creator;
    IRandomAccessStream stream;
    IOutputStream outputStream;
    DataWriter dw;
 
    creator = await folder.CreateFileAsync(fileName, collisionOption);
 
    stream = await creator.OpenAsync(FileAccessMode.ReadWrite);
    outputStream = stream.GetOutputStreamAt(0);
 
    dw = new DataWriter(outputStream);
 
    writer(dw);
 
    await dw.StoreAsync();
    bool success = await outputStream.FlushAsync();
    if (complete != null)
    {
        complete(success);
    }
}
 
public static async void Write(string folderPath, string fileName,
        CreationCollisionOption collisionOption, Action<DataWriter> writer,
        Action<bool> complete = null)
{
    StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folderPath);
 
    Write(folder, fileName, collisionOption, writer, complete);
}

Notice how the Write code allows us to pass in a delegate that is called once the writing is complete. This is because when you write your content out, it may get buffered and may not get committed right away. The completed delegate will be called once all of the content has been safely saved away. This might be important if you want to disable the Save button in your UI until the previous Save has completely finished.

We can now write back to our file using this code, in the same simple way our Read operation did:

FileAsync.Write(KnownFolders.DocumentsLibrary, "myfile.txt",
            CreationCollisionOption.ReplaceExisting, w =>
                {
                    w.WriteString(this.EditBox.Text);
                });

While asynchronous file I/O is still quite daunting in the Windows 8 Developer Preview, you can see how with a little work up front, it can be made quite elegant, while still retaining all of the power of its asynchronous nature.

In part 2 of this article we will look at how to make the reading and writing operations themselves be asynchronous too.

One Response to Simple async file access using Windows 8

  1. What’s up, just wanted to mention, I liked this post. It was practical. Keep on posting!

Trackbacks/Pingbacks

  1. FileAPI | What I learned

Leave a Reply

*