
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf


664 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
Table 20-3. Continued
Member |
Meaning in Life |
MoveTo() |
Moves a directory and its contents to a new path |
Parent |
Retrieves the parent directory of the specified path |
Root |
Gets the root portion of a path |
|
|
You begin working with the DirectoryInfo type by specifying a particular directory path as a constructor parameter. If you want to obtain access to the current working directory (i.e., the directory of the executing application), use the "." notation. Here are some examples:
//Bind to the current working directory.
DirectoryInfo dir1 = new DirectoryInfo(".");
//Bind to C:\Windows,
//using a verbatim string.
DirectoryInfo dir2 = new DirectoryInfo(@"C:\Windows");
In the second example, you are making the assumption that the path passed into the constructor (C:\Windows) already exists on the physical machine. However, if you attempt to interact with a nonexistent directory, a System.IO.DirectoryNotFoundException is thrown. Thus, if you specify a directory that is not yet created, you will need to call the Create() method before proceeding:
// Bind to a nonexistent directory, then create it.
DirectoryInfo dir3 = new DirectoryInfo(@"C:\MyCode\Testing"); dir3.Create();
Once you have created a DirectoryInfo object, you can investigate the underlying directory contents using any of the properties inherited from FileSystemInfo. To illustrate, create a new Console Application named DirectoryApp. Update your Program class with a new static method that creates a new DirectoryInfo object mapped to C:\Windows (adjust your path if need be) that displays a number of interesting statistics (see Figure 20-2 for output):
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with Directory(Info) *****\n");
ShowWindowsDirectoryInfo();
Console.ReadLine();
}
static void ShowWindowsDirectoryInfo()
{
// Dump directory information.
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows"); Console.WriteLine("***** Directory Info *****");
Console.WriteLine("FullName: {0}", dir.FullName); Console.WriteLine("Name: {0}", dir.Name); Console.WriteLine("Parent: {0}", dir.Parent); Console.WriteLine("Creation: {0}", dir.CreationTime); Console.WriteLine("Attributes: {0}", dir.Attributes); Console.WriteLine("Root: {0}", dir.Root); Console.WriteLine("**************************\n");
}
}

CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE |
665 |
Figure 20-2. Information about your Windows directory
Enumerating Files with the DirectoryInfo Type
In addition to obtaining basic details of an existing directory, you can extend the current example to use some methods of the DirectoryInfo type. First, let’s leverage the GetFiles() method to obtain information about all *.jpg files located under the C:\Windows\Web\Wallpaper directory.
■Note If your machine does not have a C:\Windows\Web\Wallpaper directory, retrofit this code to read files of a directory on your machine (for example, to read all *.bmp files from the C:\Windows directory).
This method returns an array of FileInfo types, each of which exposes details of a particular file (full details of the FileInfo type are explored later in this chapter). Assume the following static method of the Program class, called from within Main():
static void DisplayImageFiles()
{
DirectoryInfo dir = new DirectoryInfo(@"C:\Windows\Web\Wallpaper");
//Get all files with a *.jpg extension.
FileInfo[] imageFiles = dir.GetFiles("*.jpg");
//How many were found?
Console.WriteLine("Found {0} *.jpg files\n", imageFiles.Length);
// Now print out info for each file. foreach (FileInfo f in imageFiles)
{
Console.WriteLine("***************************"); Console.WriteLine("File name: {0}", f.Name); Console.WriteLine("File size: {0}", f.Length); Console.WriteLine("Creation: {0}", f.CreationTime); Console.WriteLine("Attributes: {0}", f.Attributes); Console.WriteLine("***************************\n");
}
}
Once you run the application, you see a listing something like that shown in Figure 20-3. (Your image names may vary!)

666 CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
Figure 20-3. Image file information
Creating Subdirectories with the DirectoryInfo Type
You can programmatically extend a directory structure using the DirectoryInfo. CreateSubdirectory() method. This method can create a single subdirectory, as well as multiple nested subdirectories, in a single function call. To illustrate, here is a method that extends the directory structure of the application’s install path with some custom subdirectories:
static void ModifyAppDirectory()
{
DirectoryInfo dir = new DirectoryInfo(".");
//Create \MyFolder off application directory. dir.CreateSubdirectory("MyFolder");
//Create \MyFolder2\Data off application directory. dir.CreateSubdirectory(@"MyFolder2\Data");
}
If you call this method from within Main() and examine your Windows directory using Windows Explorer, you will see that the new subdirectories are present and accounted for (see Figure 20-4).
Although you are not required to capture the return value of the CreateSubdirectory() method, be aware that a DirectoryInfo type representing the newly created item is passed back on successful execution. Consider the following update:
static void ModifyAppDirectory()
{
DirectoryInfo dir = new DirectoryInfo(".");
//Create \MyFolder off initial directory. dir.CreateSubdirectory("MyFolder");
//Capture returned DirectoryInfo object.
DirectoryInfo myDataFolder = dir.CreateSubdirectory(@"MyFolder2\Data");
// Prints path to ..\MyFolder2\Data.
Console.WriteLine("New Folder is: {0}", myDataFolder);
}

CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE |
667 |
Figure 20-4. Creating subdirectories
Working with the Directory Type
Now that you have seen the DirectoryInfo type in action, you can learn about the Directory type. For the most part, the static members of Directory mimic the functionality provided by the instance-level members defined by DirectoryInfo. Recall, however, that the members of Directory typically return string types rather than strongly typed FileInfo/DirectoryInfo types.
To illustrate some functionality of the Directory type, this final helper function displays the names of all drives mapped to the current computer (via the Directory.GetLogicalDrives() method) and uses the static Directory.Delete() method to remove the \MyFolder and \MyFolder2\Data subdirectories previously created:
static void FunWithDirectoryType()
{
//List all drives on current computer. string[] drives = Directory.GetLogicalDrives(); Console.WriteLine("Here are your drives:"); foreach (string s in drives)
Console.WriteLine("--> {0} ", s);
//Delete what was created.
Console.WriteLine("Press Enter to delete directories"); Console.ReadLine();
try
{
Directory.Delete(string.Format(@"{0}\MyFolder",
Environment.CurrentDirectory));
//The second parameter specifies whether you
//wish to destroy any subdirectories.
Directory.Delete(string.Format(@"{0}\MyFolder2", Environment.CurrentDirectory), true);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
}



670CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE
static void Main(string[] args)
{
//Make a new file on the C drive.
FileInfo f = new FileInfo(@"C:\Test.dat"); FileStream fs = f.Create();
//Use the FileStream object...
//Close down file stream.
fs.Close();
}
Notice that the FileInfo.Create() method returns a FileStream type, which exposes synchronous and asynchronous write/read operations to/from the underlying file (more details in a moment). Be aware that the FileStream object returned by FileInfo.Create() grants full read/write access to all users.
Also notice that after we are done with the current FileStream object, we make sure to close down the handle to release the underlying unmanaged resources of the stream. Given that FileStream implements IDisposable, you can make use of the C# using scope to allow the compiler to generate the teardown logic (see Chapter 8 for details):
static void Main(string[] args)
{
//Defining a 'using scope' for file I/O
//types is ideal.
FileInfo f = new FileInfo(@"C:\Test.dat"); using (FileStream fs = f.Create())
{
// Use the FileStream object...
}
}
The FileInfo.Open() Method
You can use the FileInfo.Open() method to open existing files as well as create new files with far more precision than FileInfo.Create(), given that Open() typically takes several parameters to qualify the overall structure of the file you are manipulating. Once the call to Open() completes, you are returned a FileStream object. Consider the following logic:
static void Main(string[] args)
{
// Make a new file via FileInfo.Open().
FileInfo f2 = new FileInfo(@"C:\Test2.dat"); using(FileStream fs2 = f2.Open(FileMode.OpenOrCreate,
FileAccess.ReadWrite, FileShare.None))
{
// Use the FileStream object...
}
}
This version of the overloaded Open() method requires three parameters. The first parameter specifies the general flavor of the I/O request (e.g., make a new file, open an existing file, append to a file, etc.), which is specified using the FileMode enumeration (see Table 20-5 for details):
public enum FileMode
{
CreateNew,

CHAPTER 20 ■ FILE I/O AND ISOLATED STORAGE |
671 |
Create,
Open,
OpenOrCreate,
Truncate, Append
}
Table 20-5. Members of the FileMode Enumeration
Member |
Meaning in Life |
CreateNew |
Informs the OS to make a new file. If it already exists, an IOException is thrown. |
Create |
Informs the OS to make a new file. If it already exists, it will be overwritten. |
Open |
Opens an existing file. If the file does not exist, a FileNotFoundException is |
|
thrown. |
OpenOrCreate |
Opens the file if it exists; otherwise, a new file is created. |
Truncate |
Opens a file and truncates the file to 0 bytes in size. |
Append |
Opens a file, moves to the end of the file, and begins write operations (this flag |
|
can only be used with a write-only stream). If the file does not exist, a new file is |
|
created. |
|
|
The second parameter, a value from the FileAccess enumeration, is used to determine the read/write behavior of the underlying stream:
public enum FileAccess
{
Read,
Write, ReadWrite
}
Finally, you have the third parameter, FileShare, which specifies how the file is to be shared among other file handlers. Here are the core names:
public enum FileShare
{
None,
Read,
Write, ReadWrite
}
The FileInfo.OpenRead() and FileInfo.OpenWrite() Methods
While the FileInfo.Open() method allows you to obtain a file handle in a very flexible manner, the FileInfo class also provides members named OpenRead() and OpenWrite(). As you might imagine, these methods return a properly configured read-only or write-only FileStream type, without the need to supply various enumeration values. Like FileInfo.Create() and FileInfo.Open(), OpenRead() and OpenWrite() return a FileStream object (do note that the following code assumes you have files named Test3.dat and Test4.dat on your C drive):
static void Main(string[] args)
{
// Get a FileStream object with read-only permissions.