
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
702 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
Figure 20-23. Examining the current user’s storage using storeadm.exe
The Type of System.IO.IsolatedStorage
Before examining how to write data into (and read data from) isolated storage, consider Table 20-14, which documents the core types of isolated storage. As you can see, this namespace is refreshingly small, given that the types are used in conjunction with the basic types of System.IO.
Table 20-14. The Types of System.IO.IsolatedStorage
System.IO.IsolatedStorage Type |
Meaning in Life |
IsolatedStorage |
This type represents the abstract base class from which all |
|
isolated storage implementations must derive. |
IsolatedStorageScope |
This enum controls the level of isolation to make use of |
|
(assembly, application domain, roaming). |
IsolatedStorageException |
This type specifies the exception that is thrown when an |
|
operation in isolated storage fails. |
IsolatedStorageFile |
This type represents an isolated storage area containing files and |
|
directories. |
IsolatedStorageFileStream |
This type exposes a file within isolated storage. |
|
|
Obtaining a Store Using IsolatedStorageFile
When you wish to store application data in isolated storage, the first step is to decide the level of isolation you which to establish. Recall that storage is always isolated by the current user; however, a store can also be established using assembly or application domain evidence (as well as via roaming profiles, which we will not examine here). To configure the correct isolation level, one option is


704 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
Table 20-15. Continued
Member |
Meaning in Life |
GetUserStoreForAssembly() This method obtains isolated storage corresponding to the calling code’s assembly identity.
GetUserStoreForDomain() This method obtains isolated storage corresponding to the application domain identity and assembly identity.
Remove() |
This method removes stores. |
Writing Data to Storage
Once you have obtained a store, your next task is to create an instance of the IsolatedStorageFileStream type, which represents the file in the store you will be using to persist your data. Like other IO streams, this type can be configured using the System.IO.FileMode enumeration examined earlier in this chapter. To illustrate, create a new Console Application project named SimpleIsoStorage and be sure you import the System.IO and System.IO.IsolatedStorage namespaces. Now, update Main() to call the following helper method of the Program type:
static void WriteTextToIsoStorage()
{
//Open up isolated storage based on identity of
//user + assembly evidence.
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly())
{
// Now create an IsolatedStorageFileStream type. using (IsolatedStorageFileStream isStream
=new IsolatedStorageFileStream("MyData.txt", FileMode.OpenOrCreate, store))
{
//Layer this stream into a StreamWriter
//and write out some text.
using (StreamWriter sw = new StreamWriter(isStream))
{
sw.WriteLine("This is my data."); sw.WriteLine("Cool, huh?");
}
}
}
}
Here, we begin by obtaining a store for the user based on the identity of the executing assembly (SimpleIsoStorage.exe) by calling IsolatedStorageFile.GetUserStoreForAssembly(). Next, we create a new IsolatedStorageFileStream object, specifying a new file to be named MyData.txt that will be created (or opened if it currently exists) in the store we just obtained. Finally, we layer the
IsolatedStorageFileStream object into a System.IO.StreamWriter and pump out a few lines of text. If you execute this application, you will then be able to dig into your isolated storage location
of your computer and (after a bit of hunting) discover the MyData.txt file (see Figure 20-24). If you were to open this file in notepad.exe, you would of course see the two lines of textual data.
Of course, you can layer into an IsolatedStorageFileStream object a Stream-derived type. For example, if you would rather write out data in a binary format, simply make use of the BinaryWriter rather than StreamWriter.

CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE |
705 |
Figure 20-24. Our text file placed in isolated storage
Reading Data from Storage
Reading data from a user’s store is also very simple. Consider the following new method (which I am assuming you will also call from Main() after the call to the WriteTextToIsoStorage() method) that will read the data within the MyData.txt file and display it to the console window:
private static void ReadTextFromIsoStorage()
{
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly())
{
using (IsolatedStorageFileStream isStream
=new IsolatedStorageFileStream("MyData.txt", FileMode.Open, FileAccess.Read, store))
{
// Layer into StreamReader.
using (StreamReader sr = new StreamReader(isStream))
{
string allTheData = sr.ReadToEnd(); Console.WriteLine(allTheData);
}
}
}
}
Deleting User Data from Storage
The IsolatedStorageFile type supplies two mechanisms for deleting user stores. The instance-level Remove() deletes the store that calls it. The static method IsolatedStorageFile.Remove() method takes the IsolatedStorageScope.User value and deletes all stores for the user running the code. For example, the following code deletes the active store and destroys all contents:


CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE |
707 |
■Source Code The SimpleIsoStorage project is included under the Chapter 20 subdirectory.
Isolated Storage in Action: ClickOnce Deployment
At this point, isolated storage might seem like little more than a unique approach to persisting application data on a per-user level (which is very useful in its own right). However, recall that one of the problems this API solves is how to allow applications that do not run under the umbrella of Full Trust security to persist data in a safe manner.
To close this chapter, assume you have a Windows Forms application (named FileOrIsoStorageWinApp) that defines a Form containing two buttons. Chapter 27 examines the details of the Windows Forms API; however, if you are following along, handle the Click event for each Button type.
The first button will attempt to save data to the local hard drive using standard file IO techniques (be sure to import the System.IO and System.IO.IsolatedStorage namespaces in your code file):
private void btnFileIO_Click(object sender, EventArgs e)
{
using (StreamWriter sw = new StreamWriter(@"C:\MyData.txt"))
{
sw.WriteLine("This is my data."); sw.WriteLine("Cool, huh?");
}
}
The second button will write the same data to a file in isolated storage. The implementation of this Click event handler is identical to the WriteTextToIsoStorage() method you created in the previous project; however, here it is again for your convenience:
private void btnIsoStorage_Click(object sender, EventArgs e)
{
//Open up isolated storage based on identity of
//user + assembly evidence.
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly())
{
// Now create an IsolatedStorageFileStream type. using (IsolatedStorageFileStream isStream
=new IsolatedStorageFileStream("MyData.txt", FileMode.OpenOrCreate, store))
{
//Layer this stream into a StreamWriter
//and write out some text.
using (StreamWriter sw = new StreamWriter(isStream))
{
sw.WriteLine("This is my data."); sw.WriteLine("Cool, huh?");
}
}
}
}

708 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
The IsolatedStorageFilePermission Attribute
Now, before we test our application, add the following using directive in the C# file defining your initial Form-derived type:
using System.Security.Permissions;
This namespace defines a number of security-centric attributes that can be applied to your application to inform the security subsystem which security settings a given assembly requires to operate correctly (among other details). Here, we wish to inform the CLR that our application requires, at minimum, assembly-level store isolation permissions. This can be achieved by adding the following assembly-level attribute to the code file of your Form-derived type:
[assembly: IsolatedStorageFilePermission(SecurityAction.RequestMinimum, UsageAllowed = IsolatedStorageContainment.AssemblyIsolationByUser)]
Now, when you compile and run this application directly within Visual Studio 2008 (via Ctrl+F5), you will see that clicking either button results in the creation of a new file with blobs of textual data. This is because the application has loaded from My_Computer_Zone, which as you recall grants Full Trust privileges to the assembly.
Constraining the Security Zone
Let’s deploy this application in such a way as to load it into Internet_Zone using a more restrictive permission set. To do so, we will deploy our application using ClickOnce deployment. As you might know, ClickOnce is a way to deploy an executable application to an end user’s machine via a remote web server. The remote application is hosted within an IIS virtual directory, which can be downloaded and installed to a local machine simply by using a web browser to point to the URL.
■Note Full coverage of ClickOnce is beyond the scope of this chapter. If you have never deployed an application in this manner, simply follow the instructions I provide next (and consult the .NET Framework 3.5 SDK documentation for further details if you so choose).
To begin, open your project’s Properties page by double-clicking the Properties icon of Solution Explorer. Once you have done so, click the Security tab. By default, ClickOnce applications are deployed with Full Trust, and therefore they have all the security privileges as a local application installed using traditional means.
Here, we want to build a deployment script that will force our program to run under the Internet zone, which as you recall does not allow access to the hard drive using standard file IO operations. To do so, click the Enable Click Once Security Settings check box, select the This is a partial trust application radio button, and select Internet from the zone drop-down list box. Last but not least, click the Calculate Permissions button at the bottom of the Security configuration page. This will calculate the final permission set required by your application (see Figure 20-26).


710 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
Figure 20-27. Security breach! Can’t access local file system when running in the Internet zone.
However, if you click the button that saves data to isolated storage, the application runs as expected. That wraps up our look at the isolated storage API and our introductory look at the Code Access Security model. While there is much more that could be said about CAS, as you have seen, using the types of System.IO.IsolatedStorage is very simple, as they are really just an extension of the file IO primitives.
■Source Code The FileOrIsoStorageWinApp project is included under the Chapter 20 subdirectory.
Summary
This chapter began by examining the use of the Directory(Info) and File(Info) types. As you learned, these classes allow you to manipulate a physical file or directory on your hard drive. Next, you examined a number of types derived from the abstract Stream class, specifically FileStream. Given that Stream-derived types operate on a raw stream of bytes, the System.IO namespace provides numerous reader/writer types (StreamWriter, StringWriter, BinaryWriter, etc.) that simplify the process. Along the way, you also checked out the functionality provided by DriveType, and you learned how to monitor files using the FileSystemWatcher type and how to interact with streams in an asynchronous manner.
The second part of this chapter introduced you to the topic of isolated storage. As explained, this API allows a program to read and write data in a safe sandbox, even if the application has been loaded in a constrained security environment. While the programming model is very straightforward (if you have a grasp of basic file IO), the surrounding topics add some level of complexity. Given this, you also were given a whirlwind tour of Code Access Security.
Here, you learned that assemblies present evidence to the CLR at the time they are loaded into an application domain. At this point, they are assigned a code group that has a default set of permissions. The interesting aspect of CAS as it relates to file IO is that if an application is not granted Full Trust, use of the traditional IO operations result in a security exception. However, using isolated storage, your programs can persist data on a per-user level in a safe manner.
