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.

Take Advantage of Isolated Storage with .NET

Testing the IsoStorage Class

Below is a simple console application that gets an instance of the AppItems collection, adds several AppItem objects to the collection, and then stores the collection on disk. Finally, it reads that same collection from the disk and echoes the name/value pairs to the console. The following is the complete code for this test app:

using System;
using amundsen.Utility;

public class IsoConsole
{
    public static void Main()
    {
        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));

        amundsen.Utility.IsoStorage iso = new amundsen.Utility.
                 IsoStorage();

        iso.WriteAppCollection("testing.txt",writeItems);

        AppItems readItems = iso.ReadAppCollection("testing.txt");

        for(int i=0;i<readItems.Count;i++)
        {
            AppItem item = (AppItem)readItems[i];
            Console.WriteLine(String.Format("{0}={1}",item.Name,
                                            item.Value));
        }
    }
}

Below is the resulting output when you run the above code:

[IsoStorage1.JPG]

But where, you might ask, is this information stored on the disk? It depends on which Windows OS you are using and whether it was a fresh install or an upgrade from an older OS. However, clean installs of XP and Windows 2003 store the data in a unique folder name within <SYSTEMDRIVE>\Documents and Settings\<user>\Local Settings\Application Data. They store roaming profiles in a different location. When I ran this example on my machine, the OS stored the testing.txt file in a folder called AssemFiles at this location:

C:\Documents and Settings\MCA\Local Settings\Application Data\IsolatedStorage\i3ltsz13.04j\ybsvcgd0.iwo\Url.jw0fj4okszugu4hi3vglikpjpr4ih0nd

A Safe Place for Your Data

Now you know that .NET provides a safe location for storing application- and user-specific data. You've also learned how to access this isolated storage space and how to build a simple utility class that can easily read and write name/value pairs to it.



About the Author

Mike Amundsen

An internationally known author and lecturer, Mike Amundsen travels throughout the United States and Europe speaking and teaching on a wide range of software-related topics. He has more than a dozen proramming books to his credit. When he is not working, Mike spends time with his wife and three children at their home in Kentucky, USA.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds