Code Listings
Chapter 15: Streams and I/O
Basic use of a stream:
using System;
using System.IO;
class Program
{
static void Main()
{
// Create a file called test.txt in the current directory:
using (Stream s = new FileStream ("test.txt", FileMode.Create))
{
Console.WriteLine (s.CanRead); // true
Console.WriteLine (s.CanWrite); // true
Console.WriteLine (s.CanSeek); // true
s.WriteByte (101);
s.WriteByte (102);
byte[] block = { 1, 2, 3, 4, 5 };
s.Write (block, 0, block.Length); // Write block of 5 bytes
Console.WriteLine (s.Length); // 7
Console.WriteLine (s.Position); // 7
s.Position = 0; // Move back to the start
Console.WriteLine (s.ReadByte()); // 101
Console.WriteLine (s.ReadByte()); // 102
// Read from the stream back into the block array:
Console.WriteLine (s.Read (block, 0, block.Length)); // 5
// Assuming the last Read returned 5, we'll be at
// the end of the file, so Read will now return 0:
Console.WriteLine (s.Read (block, 0, block.Length)); // 0
}
}
}
Reading/writing asynchronously:
async static void AsyncDemo()
{
using (Stream s = new FileStream ("test.txt", FileMode.Create))
{
byte[] block = { 1, 2, 3, 4, 5 };
await s.WriteAsync (block, 0, block.Length); // Write asychronously
s.Position = 0; // Move back to the start
// Read from the stream back into the block array:
Console.WriteLine (await s.ReadAsync (block, 0, block.Length)); // 5
}
}
Reading a 1000 bytes from a stream:
byte[] data = new byte [1000];
// bytesRead will always end up at 1000, unless the stream is
// itself smaller in length:
int bytesRead = 0;
int chunkSize = 1;
while (bytesRead < data.Length && chunkSize > 0)
bytesRead +=
chunkSize = s.Read (data, bytesRead, data.Length - bytesRead);
An easier way:
byte[] data = new BinaryReader (s).ReadBytes (1000);
Reading into a MemoryStream:
static MemoryStream ToMemoryStream (Stream input, bool closeInput)
{
try
{ // Read and write in
byte[] block = new byte [0x1000]; // blocks of 4K.
MemoryStream ms = new MemoryStream();
while (true)
{
int bytesRead = input.Read (block, 0, block.Length);
if (bytesRead == 0) return ms;
ms.Write (block, 0, bytesRead);
}
}
finally { if (closeInput) input.Close(); }
}
Named pipes:
// Server:
using (var s = new NamedPipeServerStream ("pipedream")
{
s.WaitForConnection();
s.WriteByte (100);
Console.WriteLine (s.ReadByte());
}
// Client:
using (var s = new NamedPipeClientStream ("pipedream"))
{
s.Connect();
Console.WriteLine (s.ReadByte());
s.WriteByte (200); // Send the value 200 back.
}
Message transmission mode:
static byte[] ReadMessage (PipeStream s)
{
MemoryStream ms = new MemoryStream();
byte[] buffer = new byte [0x1000]; // Read in 4KB blocks
do { ms.Write (buffer, 0, s.Read (buffer, 0, buffer.Length)); }
while (!s.IsMessageComplete);
return ms.ToArray();
}
// Server:
using (var s = new NamedPipeServerStream ("pipedream", PipeDirection.InOut,
1, PipeTransmissionMode.Message))
{
s.WaitForConnection();
byte[] msg = Encoding.UTF8.GetBytes ("Hello");
s.Write (msg, 0, msg.Length);
Console.WriteLine (Encoding.UTF8.GetString (ReadMessage (s)));
}
// Client:
using (var s = new NamedPipeClientStream ("pipedream"))
{
s.Connect();
s.ReadMode = PipeTransmissionMode.Message;
Console.WriteLine (Encoding.UTF8.GetString (ReadMessage (s)));
byte[] msg = Encoding.UTF8.GetBytes ("Hello right back!");
s.Write (msg, 0, msg.Length);
}
Anonymous pipes server:
string clientExe = "d:\PipeDemo\ClientDemo.exe";
HandleInheritability inherit = HandleInheritability.Inheritable;
using (var tx = new AnonymousPipeServerStream (PipeDirection.Out, inherit))
using (var rx = new AnonymousPipeServerStream (PipeDirection.In, inherit))
{
string txID = tx.GetClientHandleAsString();
string rxID = rx.GetClientHandleAsString();
var startInfo = new ProcessStartInfo (clientExe, txID + " " + rxID);
startInfo.UseShellExecute = false; // Required for child process
Process p = Process.Start (startInfo);
tx.DisposeLocalCopyOfClientHandle(); // Release unmanaged
rx.DisposeLocalCopyOfClientHandle(); // handle resources.
tx.WriteByte (100);
Console.WriteLine ("Server received: " + rx.ReadByte());
p.WaitForExit();
}
Anonymous pipes client:
static void Main (string[] args)
{
string rxID = args[0]; // Note we’re reversing the
string txID = args[1]; // receive and transmit roles.
using (var rx = new AnonymousPipeClientStream (PipeDirection.In, rxID))
using (var tx = new AnonymousPipeClientStream (PipeDirection.Out, txID))
{
Console.WriteLine ("Client received: " + rx.ReadByte());
tx.WriteByte (200);
}
}
BufferedStream:
// Write 100K to a file:
File.WriteAllBytes ("myFile.bin", new byte [100000]);
using (FileStream fs = File.OpenRead ("myFile.bin"))
using (BufferedStream bs = new BufferedStream (fs, 20000)) //20K buffer
{
bs.ReadByte();
Console.WriteLine (fs.Position); // 20000
}
StreamReader and StreamWriter:
using (FileStream fs = File.Create ("test.txt"))
using (TextWriter writer = new StreamWriter (fs))
{
writer.WriteLine ("Line1");
writer.WriteLine ("Line2");
}
using (FileStream fs = File.OpenRead ("test.txt"))
using (TextReader reader = new StreamReader (fs))
{
Console.WriteLine (reader.ReadLine()); // Line1
Console.WriteLine (reader.ReadLine()); // Line2
}
using (TextWriter writer = File.CreateText ("test.txt"))
{
writer.WriteLine ("Line1");
writer.WriteLine ("Line2");
}
using (TextWriter writer = File.AppendText ("test.txt"))
writer.WriteLine ("Line3");
using (TextReader reader = File.OpenText ("test.txt"))
while (reader.Peek() > -1)
Console.WriteLine (reader.ReadLine()); // Line1
// Line2
// Line3
using (TextWriter w = File.CreateText ("data.txt"))
{
w.WriteLine (123); // Writes "123"
w.WriteLine (true); // Writes the word "true"
}
using (TextReader r = File.OpenText ("data.txt"))
{
int myInt = int.Parse (r.ReadLine()); // myInt == 123
bool yes = bool.Parse (r.ReadLine()); // yes == true
}
Character encodings:
using (TextWriter w = File.CreateText ("but.txt")) // Use default UTF-8
w.WriteLine ("but—"); // encoding.
using (Stream s = File.OpenRead ("but.txt"))
for (int b; (b = s.ReadByte()) > -1;)
Console.WriteLine (b);
using (Stream s = File.Create ("but.txt"))
using (TextWriter w = new StreamWriter (s, Encoding.Unicode))
w.WriteLine ("but—");
foreach (byte b in File.ReadAllBytes ("but.txt"))
Console.WriteLine (b);
Binary adapaters:
public class Person
{
public string Name;
public int Age;
public double Height;
public void SaveData (Stream s)
{
BinaryWriter w = new BinaryWriter (s);
w.Write (Name);
w.Write (Age);
w.Write (Height);
w.Flush(); // Ensure the BinaryWriter buffer is cleared.
// We won't dispose/close it, so more data
} // can to be written to the stream.
public void LoadData (Stream s)
{
BinaryReader r = new BinaryReader (s);
Name = r.ReadString();
Age = r.ReadInt32();
Height = r.ReadDouble();
}
}
Compression:
using (Stream s = File.Create ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Compress))
for (byte i = 0; i < 100; i++)
ds.WriteByte (i);
using (Stream s = File.OpenRead ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Decompress))
for (byte i = 0; i < 100; i++)
Console.WriteLine (ds.ReadByte()); // Writes 0 to 99
string[] words = "The quick brown fox jumps over the lazy dog".Split();
Random rand = new Random();
using (Stream s = File.Create ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Compress))
using (TextWriter w = new StreamWriter (ds))
for (int i = 0; i < 1000; i++)
w.Write (words [rand.Next (words.Length)] + " ");
Console.WriteLine (new FileInfo ("compressed.bin").Length); // 1073
using (Stream s = File.OpenRead ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Decompress))
using (TextReader r = new StreamReader (ds))
Console.Write (r.ReadToEnd());
With asynchronous functions:
string[] words = "The quick brown fox jumps over the lazy dog".Split();
Random rand = new Random();
using (Stream s = File.Create ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Compress))
using (TextWriter w = new StreamWriter (ds))
for (int i = 0; i < 1000; i++)
await w.WriteAsync (words [rand.Next (words.Length)] + " ");
Console.WriteLine (new FileInfo ("compressed.bin").Length); // 1073
using (Stream s = File.OpenRead ("compressed.bin"))
using (Stream ds = new DeflateStream (s, CompressionMode.Decompress))
using (TextReader r = new StreamReader (ds))
Console.Write (await r.ReadToEndAsync());
Compressing in memory, option 1:
byte[] data = new byte[1000]; // We can expect a good compression
// ratio from an empty array!
MemoryStream ms = new MemoryStream();
using (Stream ds = new DeflateStream (ms, CompressionMode.Compress))
ds.Write (data, 0, data.Length);
byte[] compressed = ms.ToArray();
Console.WriteLine (compressed.Length); // 113
// Decompress back to the data array:
ms = new MemoryStream (compressed);
using (Stream ds = new DeflateStream (ms, CompressionMode.Decompress))
for (int i = 0; i < 1000; i += ds.Read (data, i, 1000 - i));
Compressing in memory, option 2:
byte[] data = new byte[1000];
MemoryStream ms = new MemoryStream();
using (Stream ds = new DeflateStream (ms, CompressionMode.Compress, true))
await ds.WriteAsync (data, 0, data.Length);
Console.WriteLine (ms.Length); // 113
ms.Position = 0;
using (Stream ds = new DeflateStream (ms, CompressionMode.Decompress))
for (int i = 0; i < 1000; i += await ds.ReadAsync (data, i, 1000 - i));
Working with Zip Files
ZipFile.CreateFromDirectory (@"d:\MyFolder", @"d:\compressed.zip");
ZipFile.ExtractToDirectory (@"d:\compressed.zip", @"d:\MyFolder");
using (ZipArchive zip = ZipFile.Open (@"d:\zz.zip", ZipArchiveMode.Read))
foreach (ZipArchiveEntry entry in zip.Entries)
Console.WriteLine (entry.FullName + " " + entry.Length);
byte[] data = File.ReadAllBytes (@"d:\foo.dll");
using (ZipArchive zip = ZipFile.Open (@"d:\zz.zip", ZipArchiveMode.Update))
zip.CreateEntry (@"bin\X64\foo.dll").Open().Write (data, 0, data.Length);
Compressing a directory:
static uint CompressFolder (string folder, bool recursive)
{
string path = "Win32_Directory.Name='" + folder + "'";
using (ManagementObject dir = new ManagementObject (path))
using (ManagementBaseObject p = dir.GetMethodParameters ("CompressEx"))
{
p ["Recursive"] = recursive;
using (ManagementBaseObject result = dir.InvokeMethod ("CompressEx",
p, null))
return (uint) result.Properties ["ReturnValue"].Value;
}
}
Determining if a volume supports compression & encryption:
using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
class SupportsCompressionEncryption
{
const int SupportsCompression = 0x10;
const int SupportsEncryption = 0x20000;
[DllImport ("Kernel32.dll", SetLastError = true)]
extern static bool GetVolumeInformation (string vol, StringBuilder name,
int nameSize, out uint serialNum, out uint maxNameLen, out uint flags,
StringBuilder fileSysName, int fileSysNameSize);
static void Main()
{
uint serialNum, maxNameLen, flags;
bool ok = GetVolumeInformation (@"C:\", null, 0, out serialNum,
out maxNameLen, out flags, null, 0);
if (!ok)
throw new Exception
("Error: Win32 code=" + Marshal.GetLastWin32Error());
bool canCompress = (flags & SupportsCompression) > 0;
bool canEncrypt = (flags & SupportsEncryption) > 0;
}
}
File security:
using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;
...
FileSecurity sec = File.GetAccessControl (@"d:\test.txt");
AuthorizationRuleCollection rules = sec.GetAccessRules (true, true,
typeof (NTAccount));
foreach (FileSystemAccessRule rule in rules)
{
Console.WriteLine (rule.AccessControlType); // Allow or Deny
Console.WriteLine (rule.FileSystemRights); // e.g., FullControl
Console.WriteLine (rule.IdentityReference.Value); // e.g., MyDomain/Joe
}
var sid = new SecurityIdentifier (WellKnownSidType.BuiltinUsersSid, null);
string usersAccount = sid.Translate (typeof (NTAccount)).ToString();
FileSystemAccessRule newRule = new FileSystemAccessRule
(usersAccount, FileSystemRights.ExecuteFile, AccessControlType.Allow);
sec.AddAccessRule (newRule);
File.SetAccessControl (@"d:\test.txt", sec);
FileInfo and DirectoryInfo:
FileInfo fi = new FileInfo (@"c:\temp\FileInfo.txt");
Console.WriteLine (fi.Exists); // false
using (TextWriter w = fi.CreateText())
w.Write ("Some text");
Console.WriteLine (fi.Exists); // false (still)
fi.Refresh();
Console.WriteLine (fi.Exists); // true
Console.WriteLine (fi.Name); // FileInfo.txt
Console.WriteLine (fi.FullName); // c:\temp\FileInfo.txt
Console.WriteLine (fi.DirectoryName); // c:\temp
Console.WriteLine (fi.Directory.Name); // temp
Console.WriteLine (fi.Extension); // .txt
Console.WriteLine (fi.Length); // 9
fi.Encrypt();
fi.Attributes ^= FileAttributes.Hidden; // (Toggle hidden flag)
fi.IsReadOnly = true;
Console.WriteLine (fi.Attributes); // ReadOnly,Archive,Hidden,Encrypted
Console.WriteLine (fi.CreationTime);
fi.MoveTo (@"c:\temp\FileInfoX.txt");
DirectoryInfo di = fi.Directory;
Console.WriteLine (di.Name); // temp
Console.WriteLine (di.FullName); // c:\temp
Console.WriteLine (di.Parent.FullName); // c:\
di.CreateSubdirectory ("SubFolder");
Enumerating files and subdirectories:
DirectoryInfo di = new DirectoryInfo (@"e:\photos");
foreach (FileInfo fi in di.GetFiles ("*.jpg"))
Console.WriteLine (fi.Name);
foreach (DirectoryInfo subDir in di.GetDirectories())
Console.WriteLine (subDir.FullName);
Application Data
string localAppDataPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
"MyCoolApplication");
if (!Directory.Exists (localAppDataPath))
Directory.CreateDirectory (localAppDataPath);
Common Application Data - permissions
public void AssignUsersFullControlToFolder (string path)
{
try
{
var sec = Directory.GetAccessControl (path);
if (UsersHaveFullControl (sec)) return;
var rule = new FileSystemAccessRule (
GetUsersAccount().ToString(),
FileSystemRights.FullControl,
InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
PropagationFlags.None,
AccessControlType.Allow);
sec.AddAccessRule (rule);
Directory.SetAccessControl (path, sec);
}
catch (UnauthorizedAccessException)
{
// Folder was already created by another user
}
}
bool UsersHaveFullControl (FileSystemSecurity sec)
{
var usersAccount = GetUsersAccount();
var rules = sec.GetAccessRules (true, true, typeof (NTAccount))
.OfType<FileSystemAccessRule>();
return rules.Any (r =>
r.FileSystemRights == FileSystemRights.FullControl &&
r.AccessControlType == AccessControlType.Allow &&
r.InheritanceFlags == (InheritanceFlags.ContainerInherit |
InheritanceFlags.ObjectInherit) &&
r.IdentityReference == usersAccount);
}
NTAccount GetUsersAccount()
{
var sid = new SecurityIdentifier (WellKnownSidType.BuiltinUsersSid, null);
return (NTAccount)sid.Translate (typeof (NTAccount));
}
Querying volume information:
DriveInfo c = new DriveInfo ("C"); // Query the C: drive.
long totalSize = c.TotalSize; // Size in bytes.
long freeBytes = c.TotalFreeSpace; // Ignores disk quotas.
long freeToMe = c.AvailableFreeSpace; // Takes quotas into account.
foreach (DriveInfo d in DriveInfo.GetDrives()) // All defined drives.
{
Console.WriteLine (d.Name); // C:\
Console.WriteLine (d.DriveType); // Fixed
Console.WriteLine (d.RootDirectory); // C:\
if (d.IsReady) // If the drive is not ready, the following two
// properties will throw exceptions:
{
Console.WriteLine (d.VolumeLabel); // The Sea Drive
Console.WriteLine (d.DriveFormat); // NTFS
}
}
Catching filesystem events:
static void Main() { Watch (@"c:\temp", "*.txt", true); }
static void Watch (string path, string filter, bool includeSubDirs)
{
using (FileSystemWatcher watcher = new FileSystemWatcher (path, filter))
{
watcher.Created += FileCreatedChangedDeleted;
watcher.Changed += FileCreatedChangedDeleted;
watcher.Deleted += FileCreatedChangedDeleted;
watcher.Renamed += FileRenamed;
watcher.Error += FileError;
watcher.IncludeSubdirectories = includeSubDirs;
watcher.EnableRaisingEvents = true;
Console.WriteLine ("Listening for events - press <enter> to end");
Console.ReadLine();
}
// Disposing the FileSystemWatcher stops further events from firing.
}
static void FileCreatedChangedDeleted (object o, FileSystemEventArgs e)
{
Console.WriteLine ("File {0} has been {1}", e.FullPath, e.ChangeType);
}
static void FileRenamed (object o, RenamedEventArgs e)
{
Console.WriteLine ("Renamed: {0}->{1}", e.OldFullPath, e.FullPath);
}
static void FileError (object o, ErrorEventArgs e)
{
Console.WriteLine ("Error: " + e.GetException().Message);
}
Directory listing in WinRT
StorageFolder docsFolder = KnownFolders.DocumentsLibrary;
IReadOnlyList files = await docsFolder.GetFilesAsync();
foreach (IStorageFile file in files)
Debug.WriteLine (file.Name);
...with filter:
StorageFolder docsFolder = KnownFolders.DocumentsLibrary;
var queryOptions = new QueryOptions (CommonFileQuery.DefaultQuery,
new[] { ".txt" });
var txtFiles = await docsFolder.CreateFileQueryWithOptions (queryOptions)
.GetFilesAsync();
foreach (StorageFile file in txtFiles)
Debug.WriteLine (file.Name);
Working with WinRT files
StorageFolder docsFolder = KnownFolders.DocumentsLibrary;
StorageFile file = await docsFolder.GetFileAsync ("foo.txt");
StorageFolder docsFolder = KnownFolders.DocumentsLibrary;
StorageFile file = await docsFolder.CreateFileAsync
("test.txt", CreationCollisionOption.ReplaceExisting);
using (Stream stream = await file.OpenStreamForWriteAsync())
using (StreamWriter writer = new StreamWriter (stream))
await writer.WriteLineAsync ("This is a test");
StorageFolder docsFolder = KnownFolders.DocumentsLibrary;
StorageFile file = await docsFolder.GetFileAsync ("test.txt");
using (var stream = await file.OpenStreamForReadAsync ())
using (StreamReader reader = new StreamReader (stream))
Debug.WriteLine (await reader.ReadToEndAsync());
Memory-mapped files
File.WriteAllBytes ("long.bin", new byte [1000000]);
using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile ("long.bin"))
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
accessor.Write (500000, (byte) 77);
Console.WriteLine (accessor.ReadByte (500000)); // 77
}
Memory-mapped files and shared memory
using (MemoryMappedFile mmFile = MemoryMappedFile.CreateNew ("Demo", 500))
using (MemoryMappedViewAccessor accessor = mmFile.CreateViewAccessor())
{
accessor.Write (0, 12345);
Console.ReadLine(); // Keep shared memory alive until user hits Enter.
}
// This can run in a separate EXE:
using (MemoryMappedFile mmFile = MemoryMappedFile.OpenExisting ("Demo"))
using (MemoryMappedViewAccessor accessor = mmFile.CreateViewAccessor())
Console.WriteLine (accessor.ReadInt32 (0)); // 12345
Working with View Accessors
byte[] data = Encoding.UTF8.GetBytes ("This is a test");
accessor.Write (0, data.Length);
accessor.WriteArray (4, data, 0, data.Length);
byte[] data = new byte [accessor.ReadInt32 (0)];
accessor.ReadArray (4, data, 0, data.Length);
Console.WriteLine (Encoding.UTF8.GetString (data)); // This is a test
struct Data { public int X, Y; }
...
var data = new Data { X = 123, Y = 456 };
accessor.Write (0, ref data);
accessor.Read (0, out data);
Console.WriteLine (data.X + " " + data.Y); // 123 456
unsafe
{
byte* pointer = null;
accessor.SafeMemoryMappedViewHandle.AcquirePointer (ref pointer);
int* intPointer = (int*) pointer;
Console.WriteLine (*intPointer); // 123
}
Reading and writing isolated storage:
// IsolatedStorage classes live in System.IO.IsolatedStorage
using (IsolatedStorageFile f =
IsolatedStorageFile.GetMachineStoreForDomain())
using (var s = new IsolatedStorageFileStream ("hi.txt",FileMode.Create,f))
using (var writer = new StreamWriter (s))
writer.WriteLine ("Hello, World");
// Read it back:
using (IsolatedStorageFile f =
IsolatedStorageFile.GetMachineStoreForDomain())
using (var s = new IsolatedStorageFileStream ("hi.txt", FileMode.Open, f))
using (var reader = new StreamReader (s))
Console.WriteLine (reader.ReadToEnd()); // Hello, world
Roaming user isolation:
var flags = IsolatedStorageScope.Assembly
| IsolatedStorageScope.User
| IsolatedStorageScope.Roaming;
using (IsolatedStorageFile f = IsolatedStorageFile.GetStore (flags,
null, null))
using (var s = new IsolatedStorageFileStream ("a.txt", FileMode.Create, f))
using (var writer = new StreamWriter (s))
writer.WriteLine ("Hello, World");
Enumerating isolated storage:
using (IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForDomain())
{
using (var s = new IsolatedStorageFileStream ("f1.x",FileMode.Create,f))
s.WriteByte (123);
using (var s = new IsolatedStorageFileStream ("f2.x",FileMode.Create,f))
s.WriteByte (123);
foreach (string s in f.GetFileNames ("*.*"))
Console.Write (s + " "); // f1.x f2.x
}
Creating and removing subdirectories:
using (IsolatedStorageFile f = IsolatedStorageFile.GetUserStoreForDomain())
{
f.CreateDirectory ("subfolder");
foreach (string s in f.GetDirectoryNames ("*.*"))
Console.WriteLine (s); // subfolder
using (var s = new IsolatedStorageFileStream (@"subfolder\sub1.txt",
FileMode.Create, f))
s.WriteByte (100);
f.DeleteFile (@"subfolder\sub1.txt");
f.DeleteDirectory ("subfolder");
}
Enumerating over other applications' stores:
System.Collections.IEnumerator rator =
IsolatedStorageFile.GetEnumerator (IsolatedStorageScope.User);
while (rator.MoveNext())
{
var isf = (IsolatedStorageFile) rator.Current;
Console.WriteLine (isf.AssemblyIdentity); // Strong name or URI
Console.WriteLine (isf.CurrentSize);
Console.WriteLine (isf.Scope); // User + ...
}