ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Isolated Storage Basics

by Mike Gunderloy
04/21/2003

It's not unusual for an application to need to store some data for later use; maybe your application allows the user to set persistent options or save work in progress. But even the simple act of saving data can be fraught with dangers, in today's world. Consider these potential stumbling blocks:

Related Reading

VB.NET Core Classes in a Nutshell
By Budi Kurniawan, Ted Neward

In the past, developers have dealt with these issues using a variety of types of special-purpose code that attempts to create unique disk files or registry keys. When you add the differing disk layouts of different operating systems, user-level security, and roaming user profiles into the mix, this becomes an extremely difficult task. Fortunately, this is an area where the .NET Framework offers a new and better way to handle things. This new and better way is called isolated storage.

Overview of Isolated Storage

Isolated storage assigns each user of the system an abstract location called a data compartment. Within the data compartment, there can be one or more stores. Stores can be isolated from one another by both user and assembly. That is, if two users run the same assembly, and that assembly uses isolated storage, then the two stores created are distinct from one another. Similarly, if the same user runs two different assemblies, and the assemblies use isolated storage, then the two stores created are distinct from one another.

A store acts as a complete virtual file system. You can create and delete directories and files within a store, and read and write data to the files in the store. As a practical matter, the data from a store is kept in hidden files in the file system, as shown in Figure 1, but you don't need to worry about that when using the isolated storage interfaces. The .NET Framework takes care of the details of finding a file system location that the user has permission to use.

The .NET Framework uses obscure folder names for isolated storage.
Figure 1. The .NET Framework uses obscure folder names for isolated storage.

Mechanics of Isolated Storage

If you're familiar with the streams and stores model used by most I/O operations under the .NET Framework, you should find isolated storage code relatively easy to understand. I'll show you the code for some basic isolated-storage operations, followed by a more application-oriented example.

Obtaining a Store

To work with isolated storage, you'll first need to include the appropriate namespace declarations in your code:

Imports System.IO
Imports System.IO.IsolatedStorage

Isolated storage stores are represented by the IsolatedStorageFile object. There's a static method of this object that you can use to obtain the store for the current assembly:

' Get the isolated store for this assembly
Dim isf As IsolatedStorageFile = _
 IsolatedStorageFile.GetUserStoreForAssembly()

Creating a Directory

The IsolatedStorageFile object implements a CreateDirectory method that you can use to create directories within the store:

' Create a directory at the top level of the store
isf.CreateDirectory("Dir1")

You can create subdirectories by specifying the entire path to the subdirectory in the call. The parent directory will be created by the same call, if necessary. Note that URL-style forward slashes are used to separate directories and subdirectories:

' Create a subdirectory
isf.CreateDirectory("Dir1/Dir2")

Creating a File

Files inside of an isolated storage store are represented by an IsolatedStorageFileStream object. To create or open a file, you can call the constructor for this class with appropriate parameters. You can create a new file at any level in the directory hierarchy, but the directories must already exist:

' Create a file at the top level of the store
Dim isfs1 As IsolatedStorageFileStream = _
 New IsolatedStorageFileStream("Rootfile.txt", _
 FileMode.OpenOrCreate, FileAccess.Write, isf)

There are several overloaded constructors for the IsolatedStorageFileStream class, but you're most likely to use the one shown here. It takes four parameters:

  1. The name of the file to open or create.
  2. A FileMode constant indicating the desired action. This can be Append, Create (which overwrites any existing file), CreateNew (which throws an exception if the file exists), Open, OpenOrCreate, or Truncate (open an existing file and set its size to zero).
  3. A FileAccess constant indicating the desired access. This can be Read, Write, or ReadWrite.
  4. The IsolatedStorageFile object that represents the store where this file will be located.

Writing to a File

After you've created an IsolatedStorageFileStream to represent a file, writing to the file is exactly like writing to any other stream. At this point, you can use any of the classes in the System.IO namespace (or elsewhere) that write to streams. For example, you can use a StreamWriter class to write text directly into the file:


' Create or open a file at the top level of the store
Dim isfs1 As IsolatedStorageFileStream = _
 New IsolatedStorageFileStream("Rootfile.txt", _
 FileMode.OpenOrCreate, FileAccess.Write, isf)

' Treat it like any other stream
Dim sw As StreamWriter = New StreamWriter(isfs1)
sw.WriteLine("Isolated storage is keen.")
sw.WriteLine("You can treat it like a file.")
sw.Flush()
sw.Close()

Reading from a File

Reading from an isolated storage file is similar to reading from any other stream. For example, you can use a StreamReader object to extract information from the file, either line by line, or character by character:

' Open a file at the top level of the store
Dim isfs1 As IsolatedStorageFileStream = _
 New IsolatedStorageFileStream("Rootfile.txt", _
 FileMode.Open, FileAccess.Read, isf)

' Treat it like any other stream
Dim sr As StreamReader = New StreamReader(isfs1)
Dim sw As StringWriter = New StringWriter()
While (sr.Peek() > -1)
    sw.WriteLine(sr.ReadLine)
End While

MessageBox.Show(sw.ToString, _
 "Isolated Storage contents")

Deleting a File or Directory

The IsolatedStorageFile class also provides a method to delete a file in which you are no longer interested:

' Delete some files
isf.DeleteFile("Dir3/Dir4/Anotherfile.txt")
isf.DeleteFile("Rootfile.txt")

You can also delete directories by using the DeleteDirectory method. Note that you must delete nested directories one at a time, starting with the most deeply nested directory.

' Delete some directories
isf.DeleteDirectory("Dir1/Dir2/")
isf.DeleteDirectory("Dir1/")

Saving User Settings

As I mentioned at the start of the article, one common use of isolated storage is to save user settings. To demonstrate this, I'll show you how to equip a form with size and location persistence. Although you can stick anything you want in isolated storage, in this case I'll take advantage of the .NET Framework's hooks for easy XML serialization and deserialization of objects by starting with a class to hold the information that I want to save:


<Serializable()> _
Public Class FormSettings
    Public Top As Integer
    Public Left As Integer
    Public Height As Integer
    Public Width As Integer
End Class

The basic strategy here is to create and save a FormSettings object when the form is closed, and to retrieve the saved object when it's opened. Here's the creation code, which I've placed in the form's Closing event handler so that it will be called whenever the user is finished with the form:


Private Sub Form1_Closing(ByVal sender As Object, _
 ByVal e As System.ComponentModel.CancelEventArgs) _
 Handles MyBase.Closing
    Try
        ' Get the isolated store for this assembly
        Dim isf As IsolatedStorageFile = _
         IsolatedStorageFile.GetUserStoreForAssembly()

        ' Create or truncate the settings file
        ' This will ensure that only the object we're
        ' saving right now will be in the file
        Dim isfs1 As IsolatedStorageFileStream = _
         New IsolatedStorageFileStream("FormSettings.xml", _
         FileMode.Create, FileAccess.Write, isf)

        ' Construct an object and tell it about the form
        Dim fs As FormSettings = New FormSettings
        fs.Top = Me.Top
        fs.Left = Me.Left
        fs.Height = Me.Height
        fs.Width = Me.Width

        ' Serialize the object to the file
        Dim xs As XmlSerializer = _
        New XmlSerializer(GetType(FormSettings))
        xs.Serialize(isfs1, fs)
        isfs1.Close()

    Catch ex As Exception
        ' If settings can't be saved, next 
        ' run will just the use form defaults
    End Try
End Sub

The other end of the process happens when the form is loaded:


Private Sub Form1_Load(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles MyBase.Load
    Try
        ' Get the isolated store for this assembly
        Dim isf As IsolatedStorageFile = _
         IsolatedStorageFile.GetUserStoreForAssembly()

        ' Open the settings file
        Dim isfs1 As IsolatedStorageFileStream = _
         New IsolatedStorageFileStream("FormSettings.xml", _
         FileMode.Open, FileAccess.Read, isf)

        ' Deserialize the XML to an object
        Dim fs As FormSettings = New FormSettings
        Dim xtr As XmlTextReader = New XmlTextReader(isfs1)
        Dim xs As XmlSerializer = _
         New XmlSerializer(GetType(FormSettings))
        fs = CType(xs.Deserialize(xtr), FormSettings)

        ' And apply the settings to the form
        Me.Top = fs.Top
        Me.Left = fs.Left
        Me.Height = fs.Height
        Me.Width = fs.Width

        ' Clean up
        isfs1.Close()
    Catch ex As Exception
        ' No file found. Just run with the existing settings
    End Try
End Sub

To see this code in action, create a new Visual Basic .NET Windows application, add the FormSettings.vb class, and place the event code behind the form. You'll need to add references to the appropriate namespaces as well:


Imports System.IO
Imports System.IO.IsolatedStorage
Imports System.Xml
Imports System.Xml.Serialization

Then run the application to display the default blank form. Move it anywhere on the screen and size it as you like. Close the form to exit the application. Now run it again. You'll find that the form comes back where you left it, instead of at its default location.

Note that very little of this code is related to the actual settings that I'm storing. If I decided in the future to add the form's background color, its caption text, or any other property to the list of things to persist, I could simply add another public property to the class and one line in each event procedure. The isolated storage and serialization plumbing takes care of everything else.

Choosing Isolated Storage

As with any other technique, there are good times and bad times to use isolated storage. Here are some typical applications where isolated storage can be a good fit:

But while you're considering isolated storage, you also need to think about the potential drawbacks and pitfalls:

I think you'll find it worth taking the time to understand isolated storage, especially if you still recall the joy of trying to find a file or registry location that worked as a universal place to save settings in previous development environments. In this, as in so many other ways, the .NET Framework has done an admirable job of providing functionality that just works the way it should.

Mike Gunderloy is the lead developer for Larkware and author of numerous books and articles on programming topics.


Return to ONDotnet.com

Copyright © 2009 O'Reilly Media, Inc.