Take Advantage of Isolated Storage with .NET

Many applications commonly handle user preferences. Application developers often need to store items such as the arrangement of windows on the screen, default color selections, and other values somewhere so that the application can call them up the next time the user loads it. In addition, now that most Windows operating systems support multiple user accounts on the same machine, keeping track of this information by user is critical. Finally, developers need to be able to store it in a safe location. With the increased emphasis on trusted computing, you can’t allow all users to have complete access to any file or folder you create.

Thankfully, the .NET runtime has a ready-made solution to allow you to easily handle user-specific, trusted storage: the IsolatedStorage namespace. This article teaches some basics of the IsolatedStorage namespace and demonstrates how to use it to create a utility class that makes reading and writing user-specific application data safe and easy.

The Basics of Isolated Storage

Isolated storage describes a safe location on the disk where users—regardless of access level (guest, administrator, and so forth)—can safely read and write data. This safe space is isolated from the rest of the disk storage. That way, even guest users who have no other access can read and write some data. The System.IO.IsolatedStorage namespace contains four classes, one interface, and one enumeration. This article focuses on two of those classes: IsolatedStorageFile and IsolatedStorageFileStream. It also describes the use of the IsolatedStorageScope enumeration.

The IsolatedStorageFile class provides access to a safe place on the disk where you can read and write files, even if you are a guest user on the workstation. The IsolatedStorageFileStream class provides the actual read/write buffering for your files. The IsolatedStorageScope enumeration allows you to set the scope of the storage item. For example, you can set the scope to the assembly, the domain, the user, or a combination of these. (This article does not cover all the aspects of isolated storage, but you can search your .NET documentation for the phrase “Isolated storage” to get the full picture.)

Designing the IsoStorage Class

For this demonstration, you create a utility class (IsoStorage) with two methods (ReadAppCollection and WriteAppCollection) that handle reading and writing user data. You also create a simple class to provide name/value pair storage (AppItem) and one to handle a collection of these name/value pairs (AppItems). The following is a brief snapshot of the classes and their public members:

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace amundsen.Utility
{
    // class for storing name/value pairs
    [Serializable]
    public class AppItem
    {
        public string Name;
        public object Value;
    }

    // class for storing collection of app items
    [Serializable]
    public class AppItems : System.Collections.ArrayList {}

    // class for reading/writing collections to disk
    public class IsoStorage
    {
        public AppItems ReadAppCollection(string filename)

        public void WriteAppCollection(string filename,
                                       AppItems appCollection)
    }
}

You can see the namespaces this example uses. System.IO and System.IO.IsolatedStorage allow you to handle the reading and writing of data. The System.Collections namespace is used to gain access to the ArrayList class (you use that for your AppItems class). Finally, the remaining two namespaces are used to format the collection into a binary stream to read and write on the disk (more on that in a moment).

The AppItem and AppItems classes are very simple. They are just examples of how you can organize your storage. In the example, the storage collection is just a collection of name/value pairs stored in .NET’s ArrayList class. (This is not the most efficient way to store name/value pairs, but it’s quick and easy for this example.) The following code is all you need to be able to handle name/value pair collections:

// class for storing name/value pairs
[Serializable]
public class AppItem
{
    public string Name;
    public object Value;

    public AppItem() {}

    public AppItem(string name, object value)
    {
        Name=name;
        Value=value;
    }
}

// class for storing collection of app items
[Serializable]
public class AppItems : System.Collections.ArrayList {}

With these two classes, you will be able to easily manage name/value pairs using code like this:

AppItems writeItems = new amundsen.Utility.AppItems();
writeItems.Add(new AppItem("name","mike"));
writeItems.Add(new AppItem("amount",13.5));
writeItems.Add(new AppItem("datepaid",System.DateTime.Now));

Now, you need the code to actually read and write the collection to isolated storage. The process is pretty simple. For example, if you want to write the collection to disk, first get access to the workstation’s isolated storage space and open a file stream to that location. Next, convert the collection in memory to a series of bytes (binary serialization) and then write those bytes to the disk. The process of reading from storage is very similar. Access the storage and open the file, read the bytes from disk, and turn them back into a collection class (de-serialize the bytes). The following is the code to read and write the AppItems collection:

// class for reading/writing collections to disk
public class IsoStorage
{
    public AppItems ReadAppCollection(string filename)
    {
        try
        {
            IsolatedStorageFile  isf =
                System.IO.IsolatedStorage.IsolatedStorageFile.
                       GetStore(
                IsolatedStorageScope.User |
                IsolatedStorageScope.Assembly, null, null);
            Stream reader =
                new IsolatedStorageFileStream(filename, FileMode.
                                              Open,isf);
            IFormatter formatter = new BinaryFormatter();
            AppItems appCollection = (AppItems) formatter.
                                     Deserialize(reader);
            reader.Close();

            return appCollection;
        }
        catch (Exception ex)
        {
            throw new ApplicationException("ReadAppCollection
                                            failed",ex);
        }
    }

    public void WriteAppCollection(string filename,
                                   AppItems appCollection)
    {
        try
        {
            IsolatedStorageFile  isf =
                System.IO.IsolatedStorage.IsolatedStorageFile.
                       GetStore(
                IsolatedStorageScope.User |
                IsolatedStorageScope.Assembly, null, null);
            Stream writer =
                new IsolatedStorageFileStream(filename,
                                              FileMode.Create,isf);
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(writer,appCollection);
            writer.Close();
        }
        catch (Exception ex)
        {
            throw new ApplicationException("WriteAppCollection
                                            failed.",ex);
        }
    }
}

That’s all there is to the class library for handling simple isolated storage. Now, you can write a simple console application to test the utility.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read