Sunteți pe pagina 1din 169

Contents

File and Stream I/O


File path formats on Windows systems
Common I/O Tasks
How to: Copy Directories
How to: Enumerate Directories and Files
How to: Read and Write to a Newly Created Data File
How to: Open and Append to a Log File
How to: Write Text to a File
How to: Read Text from a File
How to: Read Characters from a String
How to: Write Characters to a String
How to: Add or Remove Access Control List Entries
How to: Compress and Extract Files
Composing Streams
How to: Convert Between .NET Framework Streams and Windows Runtime Streams
Asynchronous File I/O
Handling I/O errors
Isolated Storage
Types of Isolation
How to: Obtain Stores for Isolated Storage
How to: Enumerate Stores for Isolated Storage
How to: Delete Stores in Isolated Storage
How to: Anticipate Out-of-Space Conditions with Isolated Storage
How to: Create Files and Directories in Isolated Storage
How to: Find Existing Files and Directories in Isolated Storage
How to: Read and Write to Files in Isolated Storage
How to: Delete Files and Directories in Isolated Storage
Pipes
How to: Use Anonymous Pipes for Local Interprocess Communication
How to: Use Named Pipes for Network Interprocess Communication
Pipelines
Work with Buffers
Memory-Mapped Files
File and Stream I/O
7 minutes to read • Edit Online

File and stream I/O (input/output) refers to the transfer of data either to or from a storage medium. In the .NET
Framework, the System.IO namespaces contain types that enable reading and writing, both synchronously and
asynchronously, on data streams and files. These namespaces also contain types that perform compression and
decompression on files, and types that enable communication through pipes and serial ports.
A file is an ordered and named collection of bytes that has persistent storage. When you work with files, you
work with directory paths, disk storage, and file and directory names. In contrast, a stream is a sequence of bytes
that you can use to read from and write to a backing store, which can be one of several storage mediums (for
example, disks or memory). Just as there are several backing stores other than disks, there are several kinds of
streams other than file streams, such as network, memory, and pipe streams.

Files and directories


You can use the types in the System.IO namespace to interact with files and directories. For example, you can get
and set properties for files and directories, and retrieve collections of files and directories based on search
criteria.
For path naming conventions and the ways to express a file path for Windows systems, including with the DOS
device syntax supported in .NET Core 1.1 and later and the .NET Framework 4.6.2 and later, see File path formats
on Windows systems.
Here are some commonly used file and directory classes:
File - provides static methods for creating, copying, deleting, moving, and opening files, and helps create a
FileStream object.
FileInfo - provides instance methods for creating, copying, deleting, moving, and opening files, and helps
create a FileStream object.
Directory - provides static methods for creating, moving, and enumerating through directories and
subdirectories.
DirectoryInfo - provides instance methods for creating, moving, and enumerating through directories and
subdirectories.
Path - provides methods and properties for processing directory strings in a cross-platform manner.
You should always provide robust exception handling when calling filesystem methods. For more information,
see Handling I/O errors.
In addition to using these classes, Visual Basic users can use the methods and properties provided by the
Microsoft.VisualBasic.FileIO.FileSystem class for file I/O.
See How to: Copy Directories, How to: Create a Directory Listing, and How to: Enumerate Directories and Files.

Streams
The abstract base class Stream supports reading and writing bytes. All classes that represent streams inherit
from the Stream class. The Stream class and its derived classes provide a common view of data sources and
repositories, and isolate the programmer from the specific details of the operating system and underlying
devices.
Streams involve three fundamental operations:
Reading - transferring data from a stream into a data structure, such as an array of bytes.
Writing - transferring data to a stream from a data source.
Seeking - querying and modifying the current position within a stream.
Depending on the underlying data source or repository, a stream might support only some of these capabilities.
For example, the PipeStream class does not support seeking. The CanRead, CanWrite, and CanSeek properties
of a stream specify the operations that the stream supports.
Here are some commonly used stream classes:
FileStream – for reading and writing to a file.
IsolatedStorageFileStream – for reading and writing to a file in isolated storage.
MemoryStream – for reading and writing to memory as the backing store.
BufferedStream – for improving performance of read and write operations.
NetworkStream – for reading and writing over network sockets.
PipeStream – for reading and writing over anonymous and named pipes.
CryptoStream – for linking data streams to cryptographic transformations.
For an example of working with streams asynchronously, see Asynchronous File I/O.

Readers and writers


The System.IO namespace also provides types for reading encoded characters from streams and writing them to
streams. Typically, streams are designed for byte input and output. The reader and writer types handle the
conversion of the encoded characters to and from bytes so the stream can complete the operation. Each reader
and writer class is associated with a stream, which can be retrieved through the class's BaseStream property.
Here are some commonly used reader and writer classes:
BinaryReader and BinaryWriter – for reading and writing primitive data types as binary values.
StreamReader and StreamWriter – for reading and writing characters by using an encoding value to
convert the characters to and from bytes.
StringReader and StringWriter – for reading and writing characters to and from strings.
TextReader and TextWriter – serve as the abstract base classes for other readers and writers that read and
write characters and strings, but not binary data.
See How to: Read Text from a File, How to: Write Text to a File, How to: Read Characters from a String, and How
to: Write Characters to a String.

Asynchronous I/O operations


Reading or writing a large amount of data can be resource-intensive. You should perform these tasks
asynchronously if your application needs to remain responsive to the user. With synchronous I/O operations, the
UI thread is blocked until the resource-intensive operation has completed. Use asynchronous I/O operations
when developing Windows 8.x Store apps to prevent creating the impression that your app has stopped working.
The asynchronous members contain Async in their names, such as the CopyToAsync, FlushAsync, ReadAsync,
and WriteAsync methods. You use these methods with the async and await keywords.
For more information, see Asynchronous File I/O.

Compression
Compression refers to the process of reducing the size of a file for storage. Decompression is the process of
extracting the contents of a compressed file so they are in a usable format. The System.IO.Compression
namespace contains types for compressing and decompressing files and streams.
The following classes are frequently used when compressing and decompressing files and streams:
ZipArchive – for creating and retrieving entries in the zip archive.
ZipArchiveEntry – for representing a compressed file.
ZipFile – for creating, extracting, and opening a compressed package.
ZipFileExtensions – for creating and extracting entries in a compressed package.
DeflateStream – for compressing and decompressing streams using the Deflate algorithm.
GZipStream – for compressing and decompressing streams in gzip data format.
See How to: Compress and Extract Files.

Isolated storage
Isolated storage is a data storage mechanism that provides isolation and safety by defining standardized ways of
associating code with saved data. The storage provides a virtual file system that is isolated by user, assembly, and
(optionally) domain. Isolated storage is particularly useful when your application does not have permission to
access user files. You can save settings or files for your application in a manner that is controlled by the
computer's security policy.
Isolated storage is not available for Windows 8.x Store apps; instead, use application data classes in the
Windows.Storage namespace. For more information, see Application data.
The following classes are frequently used when implementing isolated storage:
IsolatedStorage – provides the base class for isolated storage implementations.
IsolatedStorageFile – provides an isolated storage area that contains files and directories.
IsolatedStorageFileStream - exposes a file within isolated storage.
See Isolated Storage.

I/O operations in Windows Store apps


The .NET for Windows 8.x Store apps contains many of the types for reading from and writing to streams;
however, this set does not include all the .NET Framework I/O types.
Some important differences to note when using I/O operations in Windows 8.x Store apps:
Types specifically related to file operations, such as File, FileInfo, Directory and DirectoryInfo, are not
included in the .NET for Windows 8.x Store apps. Instead, use the types in the Windows.Storage
namespace of the Windows Runtime, such as StorageFile and StorageFolder.
Isolated storage is not available; instead, use application data.
Use asynchronous methods, such as ReadAsync and WriteAsync, to prevent blocking the UI thread.
The path-based compression types ZipFile and ZipFileExtensions are not available. Instead, use the types
in the Windows.Storage.Compression namespace.
You can convert between .NET Framework streams and Windows Runtime streams, if necessary. For more
information, see How to: Convert Between .NET Framework Streams and Windows Runtime Streams or
WindowsRuntimeStreamExtensions.
For more information about I/O operations in a Windows 8.x Store app, see Quickstart: Reading and writing files.

I/O and security


When you use the classes in the System.IO namespace, you must follow operating system security requirements
such as access control lists (ACLs) to control access to files and directories. This requirement is in addition to any
FileIOPermission requirements. You can manage ACLs programmatically. For more information, see How to:
Add or Remove Access Control List Entries.
Default security policies prevent Internet or intranet applications from accessing files on the user’s computer.
Therefore, do not use the I/O classes that require a path to a physical file when writing code that will be
downloaded over the Internet or intranet. Instead, use isolated storage for traditional .NET Framework
applications, or use application data for Windows 8.x Store apps.
A security check is performed only when the stream is constructed. Therefore, do not open a stream and then
pass it to less-trusted code or application domains.

Related topics
Common I/O Tasks
Provides a list of I/O tasks associated with files, directories, and streams, and links to relevant content and
examples for each task.
Asynchronous File I/O
Describes the performance advantages and basic operation of asynchronous I/O.
Isolated Storage
Describes a data storage mechanism that provides isolation and safety by defining standardized ways of
associating code with saved data.
Pipes
Describes anonymous and named pipe operations in the .NET Framework.
Memory-Mapped Files
Describes memory-mapped files, which contain the contents of files on disk in virtual memory. You can
use memory-mapped files to edit very large files and to create shared memory for interprocess
communication.
File path formats on Windows systems
15 minutes to read • Edit Online

Members of many of the types in the System.IO namespace include a path parameter that lets you specify an
absolute or relative path to a file system resource. This path is then passed to Windows file system APIs. This topic
discusses the formats for file paths that you can use on Windows systems.

Traditional DOS paths


A standard DOS path can consist of three components:
A volume or drive letter followed by the volume separator ( : ).
A directory name. The directory separator character separates subdirectories within the nested directory
hierarchy.
An optional filename. The directory separator character separates the file path and the filename.
If all three components are present, the path is absolute. If no volume or drive letter is specified and the directory
name begins with the directory separator character, the path is relative from the root of the current drive.
Otherwise, the path is relative to the current directory. The following table shows some possible directory and file
paths.

PATH DESCRIPTION

C:\Documents\Newsletters\Summer2018.pdf An absolute file path from the root of drive C:

\Program Files\Custom Utilities\StringFinder.exe An absolute path from the root of the current drive.

2018\January.xlsx A relative path to a file in a subdirectory of the current


directory.

..\Publications\TravelBrochure.pdf A relative path to file in a directory that is a peer of the


current directory.

C:\Projects\apilibrary\apilibrary.sln An absolute path to a file from the root of drive C:

C:Projects\apilibrary\apilibrary.sln A relative path from the current directory of the C: drive.

IMPORTANT
Note the difference between the last two paths. Both specify the optional volume specifier (C: in both cases), but the first
begins with the root of the specified volume, whereas the second does not. As result, the first is an absolute path from the
root directory of drive C:, whereas the second is a relative path from the current directory of drive C:. Use of the second form
when the first is intended is a common source of bugs that involve Windows file paths.

You can determine whether a file path is fully qualified (that is, it the path is independent of the current directory
and does not change when the current directory changes) by calling the IsPathFullyQualified method. Note that
such a path can include relative directory segments ( . and .. ) and still be fully qualified if the resolved path
always points to the same location.
The following example illustrates the difference between absolute and relative paths. It assumes that the directory
D:\FY2018\ exists, and that you haven't set any current directory for D:\ from the command prompt before
running the example.

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

public class Example


{
public static void Main(string[] args)
{
Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'");
Console.WriteLine("Setting current directory to 'C:\\'");

Directory.SetCurrentDirectory(@"C:\");
string path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");

Console.WriteLine("Setting current directory to 'D:\\Docs'");


Directory.SetCurrentDirectory(@"D:\Docs");

path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");

// This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
Console.WriteLine($"'D:FY2018' resolves to {path}");

Console.WriteLine("Setting current directory to 'C:\\'");


Directory.SetCurrentDirectory(@"C:\");

path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");

// This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
// the command prompt set the current directory before launch of our application, which
// sets a hidden environment variable that is considered.
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");

if (args.Length < 1)
{
Console.WriteLine(@"Launching again, after setting current directory to D:\FY2018");
Uri currentExe = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute);
string commandLine = $"/C cd D:\\FY2018 & \"{currentExe.LocalPath}\" stop";
ProcessStartInfo psi = new ProcessStartInfo("cmd", commandLine); ;
Process.Start(psi).WaitForExit();

Console.WriteLine("Sub process returned:");


path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");
}
Console.WriteLine("Press any key to continue... ");
Console.ReadKey();
}
}
// The example displays the following output:
// Current directory is 'C:\Programs\file-paths'
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// Setting current directory to 'D:\Docs'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\Docs\FY2018
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// Launching again, after setting current directory to D:\FY2018
// Sub process returned:
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// The subprocess displays the following output:
// Current directory is 'C:\'
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\FY2018\FY2018
// Setting current directory to 'D:\Docs'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\Docs\FY2018
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\FY2018\FY2018

Imports System.Diagnostics
Imports System.IO
Imports System.Reflection

Public Module Example

Public Sub Main(args() As String)


Console.WriteLine($"Current directory is '{Environment.CurrentDirectory}'")
Console.WriteLine("Setting current directory to 'C:\'")
Directory.SetCurrentDirectory("C:\")

Dim filePath As String = Path.GetFullPath("D:\FY2018")


Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
filePath = Path.GetFullPath("D:FY2018")
Console.WriteLine($"'D:FY2018' resolves to {filePath}")

Console.WriteLine("Setting current directory to 'D:\\Docs'")


Directory.SetCurrentDirectory("D:\Docs")

filePath = Path.GetFullPath("D:\FY2018")
Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
filePath = Path.GetFullPath("D:FY2018")

' This will be "D:\Docs\FY2018" as it happens to match the drive of the current directory
Console.WriteLine($"'D:FY2018' resolves to {filePath}")

Console.WriteLine("Setting current directory to 'C:\\'")


Directory.SetCurrentDirectory("C:\")

filePath = Path.GetFullPath("D:\FY2018")
Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")

' This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the subprocess. In the sub process,
' the command prompt set the current directory before launch of our application, which
' sets a hidden environment variable that is considered.
filePath = Path.GetFullPath("D:FY2018")
Console.WriteLine($"'D:FY2018' resolves to {filePath}")

If args.Length < 1 Then


Console.WriteLine("Launching again, after setting current directory to D:\FY2018")
Dim currentExe As New Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute)
Dim commandLine As String = $"/C cd D:\FY2018 & ""{currentExe.LocalPath}"" stop"
Dim psi As New ProcessStartInfo("cmd", commandLine)
Process.Start(psi).WaitForExit()

Console.WriteLine("Sub process returned:")


filePath = Path.GetFullPath("D:\FY2018")
filePath = Path.GetFullPath("D:\FY2018")
Console.WriteLine($"'D:\\FY2018' resolves to {filePath}")
filePath = Path.GetFullPath("D:FY2018")
Console.WriteLine($"'D:FY2018' resolves to {filePath}")
End If
Console.WriteLine("Press any key to continue... ")
Console.ReadKey()
End Sub
End Module
' The example displays the following output:
' Current directory is 'C:\Programs\file-paths'
' Setting current directory to 'C:\'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to d:\FY2018
' Setting current directory to 'D:\Docs'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to D:\Docs\FY2018
' Setting current directory to 'C:\'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to d:\FY2018
' Launching again, after setting current directory to D:\FY2018
' Sub process returned:
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to d:\FY2018
' The subprocess displays the following output:
' Current directory is 'C:\'
' Setting current directory to 'C:\'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to D:\FY2018\FY2018
' Setting current directory to 'D:\Docs'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to D:\Docs\FY2018
' Setting current directory to 'C:\'
' 'D:\FY2018' resolves to D:\FY2018
' 'D:FY2018' resolves to D:\FY2018\FY2018

UNC paths
Universal naming convention (UNC ) paths, which are used to access network resources, have the following format:
A server or host name, which is prefaced by \\. The server name can be a NetBIOS machine name or an
IP/FQDN address (IPv4 as well as v6 are supported).
A share name, which is separated from the host name by \. Together, the server and share name make up the
volume.
A directory name. The directory separator character separates subdirectories within the nested directory
hierarchy.
An optional filename. The directory separator character separates the file path and the filename.
The following are some examples of UNC paths:

PATH DESCRIPTION

\\system07\C$\ The root directory of the C: drive on system07 .

\\Server2\Share\Test\Foo.txt The Foo.txt file in the Test directory of the \\Server2\Share


volume.

UNC paths must always be fully qualified. They can include relative directory segments ( . and .. ), but these
must be part of a fully qualified path. You can use relative paths only by mapping a UNC path to a drive letter.
DOS device paths
The Windows operating system has a unified object model that points to all resources, including files. These object
paths are accessible from the console window and are exposed to the Win32 layer through a special folder of
symbolic links that legacy DOS and UNC paths are mapped to. This special folder is accessed via the DOS device
path syntax, which is one of:
\\.\C:\Test\Foo.txt \\?\C:\Test\Foo.txt

In addition to identifying a drive by its drive letter, you can identify a volume by using its volume GUID. This takes
the form:
\\.\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt
\\?\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt

NOTE
DOS device path syntax is supported on .NET implementations running on Windows starting with .NET Core 1.1 and .NET
Framework 4.6.2.

The DOS device path consists of the following components:


The device path specifier ( \\.\ or \\?\ ), which identifies the path as a DOS device path.

NOTE
The \\?\ is supported in all versions of .NET Core and in the .NET Framework starting with version 4.6.2.

A symbolic link to the "real" device object (C: in the case of a drive name, or Volume{b75e2c83-0000-0000-
0000-602f00000000} in the case of a volume GUID ).
The first segment of the DOS device path after the device path specifier identifies the volume or drive. (For
example, \\?\C:\ and \\.\BootPartition\ .)
There is a specific link for UNCs that is called, not surprisingly, UNC . For example:
\\.\UNC\Server\Share\Test\Foo.txt \\?\UNC\Server\Share\Test\Foo.txt

For device UNCs, the server/share portion forms the volume. For example, in
\\?\server1\e:\utilities\\filecomparer\ , the server/share portion is server1\utilities. This is significant
when calling a method such as Path.GetFullPath(String, String) with relative directory segments; it is never
possible to navigate past the volume.
DOS device paths are fully qualified by definition. Relative directory segments ( . and .. ) are not allowed.
Current directories never enter into their usage.

Example: Ways to refer to the same file


The following example illustrates some of the ways in which you can refer to a file when using the APIs in the
System.IO namespace. The example instantiates a FileInfo object and uses its Name and Length properties to
display the filename and the length of the file.
using System;
using System.IO;

class Program
{
static void Main()
{
string[] filenames = {
@"c:\temp\test-file.txt",
@"\\127.0.0.1\c$\temp\test-file.txt",
@"\\LOCALHOST\c$\temp\test-file.txt",
@"\\.\c:\temp\test-file.txt",
@"\\?\c:\temp\test-file.txt",
@"\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
@"\\127.0.0.1\c$\temp\test-file.txt" };

foreach (var filename in filenames)


{
FileInfo fi = new FileInfo(filename);
Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes");
}
}
}
// The example displays output like the following:
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes

Imports System.IO

Module Program
Sub Main()
Dim filenames() As String = {
"c:\temp\test-file.txt",
"\\127.0.0.1\c$\temp\test-file.txt",
"\\LOCALHOST\c$\temp\test-file.txt",
"\\.\c:\temp\test-file.txt",
"\\?\c:\temp\test-file.txt",
"\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
"\\127.0.0.1\c$\temp\test-file.txt" }

For Each filename In filenames


Dim fi As New FileInfo(filename)
Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes")
Next
End Sub
End Module

Path normalization
Almost all paths passed to Windows APIs are normalized. During normalization, Windows performs the following
steps:
Identifies the path.
Applies the current directory to partially qualified (relative) paths.
Canonicalizes component and directory separators.
Evaluates relative directory components ( . for the current directory and .. for the parent directory).
Trims certain characters.
This normalization happens implicitly, but you can do it explicitly by calling the Path.GetFullPath method, which
wraps a call to the GetFullPathName() function. You can also call the Windows GetFullPathName() function
directly using P/Invoke.
Identifying the path
The first step in path normalization is identifying the type of path. Paths fall into one of a few categories:
They are device paths; that is, they begin with two separators and a question mark or period ( \\? or \\. ).
They are UNC paths; that is, they begin with two separators without a question mark or period.
They are fully qualified DOS paths; that is, they begin with a drive letter, a volume separator, and a component
separator ( C:\ ).
They designate a legacy device ( CON , LPT1 ).
They are relative to the root of the current drive; that is, they begin with a single component separator ( \ ).
They are relative to the current directory of a specified drive; that is, they begin with a drive letter, a volume
separator, and no component separator ( C: ).
They are relative to the current directory; that is, they begin with anything else ( temp\testfile.txt ).

The type of the path determines whether or not a current directory is applied in some way. It also determines what
the "root" of the path is.
Handling legacy devices
If the path is a legacy DOS device such as CON , COM1 , or LPT1 , it is converted into a device path by prepending
\\.\ and returned.

A path that begins with a legacy device name is always interpreted as a legacy device by the
Path.GetFullPath(String) method. For example, the DOS device path for CON.TXT is \\.\CON , and the DOS device
path for COM1.TXT\file1.txt is \\.\COM1 .
Applying the current directory
If a path isn't fully qualified, Windows applies the current directory to it. UNCs and device paths do not have the
current directory applied. Neither does a full drive with separator C:\.
If the path starts with a single component separator, the drive from the current directory is applied. For example, if
the file path is \utilities and the current directory is C:\temp\ , normalization produces C:\utilities .
If the path starts with a drive letter, volume separator, and no component separator, the last current directory set
from the command shell for the specified drive is applied. If the last current directory was not set, the drive alone is
applied. For example, if the file path is D:sources , the current directory is C:\Documents\ , and the last current
directory on drive D: was D:\sources\ , the result is D:\sources\sources . These "drive relative" paths are a common
source of program and script logic errors. Assuming that a path beginning with a letter and a colon isn't relative is
obviously not correct.
If the path starts with something other than a separator, the current drive and current directory are applied. For
example, if the path is filecompare and the current directory is C:\utilities\ , the result is
C:\utilities\filecompare\ .

IMPORTANT
Relative paths are dangerous in multithreaded applications (that is, most applications) because the current directory is a per-
process setting. Any thread can change the current directory at any time. Starting with .NET Core 2.1, you can call the
Path.GetFullPath(String, String) method to get an absolute path from a relative path and the base path (the current
directory) that you want to resolve it against.
Canonicalizing separators
All forward slashes ( / ) are converted into the standard Windows separator, the back slash ( \ ). If they are
present, a series of slashes that follow the first two slashes are collapsed into a single slash.
Evaluating relative components
As the path is processed, any components or segments that are composed of a single or a double period ( . or
.. ) are evaluated:

For a single period, the current segment is removed, since it refers to the current directory.
For a double period, the current segment and the parent segment are removed, since the double period
refers to the parent directory.
Parent directories are only removed if they aren't past the root of the path. The root of the path depends on
the type of path. It is the drive ( C:\ ) for DOS paths, the server/share for UNCs ( \\Server\Share ), and the
device path prefix for device paths ( \\?\ or \\.\ ).
Trimming characters
Along with the runs of separators and relative segments removed earlier, some additional characters are removed
during normalization:
If a segment ends in a single period, that period is removed. (A segment of a single or double period is
normalized in the previous step. A segment of three or more periods is not normalized and is actually a
valid file/directory name.)
If the path doesn't end in a separator, all trailing periods and spaces (U+0020) are removed. If the last
segment is simply a single or double period, it falls under the relative components rule above.
This rule means that you can create a directory name with a trailing space by adding a trailing separator
after the space.

IMPORTANT
You should never create a directory or filename with a trailing space. Trailing spaces can make it difficult or
impossible to access a directory, and applications commonly fail when attempting to handle directories or files whose
names include trailing spaces.

Skipping normalization
Normally, any path passed to a Windows API is (effectively) passed to the GetFullPathName function and
normalized. There is one important exception: a device path that begins with a question mark instead of a period.
Unless the path starts exactly with \\?\ (note the use of the canonical backslash), it is normalized.
Why would you want to skip normalization? There are three major reasons:
1. To get access to paths that are normally unavailable but are legal. A file or directory called hidden. , for
example, is impossible to access in any other way.
2. To improve performance by skipping normalization if you've already normalized.
3. On the .NET Framework only, to skip the MAX_PATH check for path length to allow for paths that are greater
than 259 characters. Most APIs allow this, with some exceptions.
NOTE
.NET Core handles long paths implicitly and does not perform a MAX_PATH check. The MAX_PATH check applies only to the
.NET Framework.

Skipping normalization and max path checks is the only difference between the two device path syntaxes; they are
otherwise identical. Be careful with skipping normalization, since you can easily create paths that are difficult for
"normal" applications to deal with.
Paths that start with \\?\ are still normalized if you explicitly pass them to the GetFullPathName function.
You can pass paths of more than MAX_PATH characters to GetFullPathName without \\?\ . It supports arbitrary
length paths up to the maximum string size that Windows can handle.

Case and the Windows file system


A peculiarity of the Windows file system that non-Windows users and developers find confusing is that path and
directory names are case-insensitive. That is, directory and file names reflect the casing of the strings used when
they are created. For example, the method call

Directory.Create("TeStDiReCtOrY");

Directory.Create("TeStDiReCtOrY")

creates a directory named TeStDiReCtOrY. If you rename a directory or file to change its case, the directory or file
name reflects the case of the string used when you rename it. For example, the following code renames a file
named test.txt to Test.txt:

using System.IO;

class Example
{
static void Main()
{
var fi = new FileInfo(@".\test.txt");
fi.MoveTo(@".\Test.txt");
}
}

Imports System.IO

Module Example
Public Sub Main()
Dim fi As New FileInfo(".\test.txt")
fi.MoveTo(".\Test.txt")
End Sub
End Module

However, directory and file name comparisons are case-insensitive. If you search for a file named "test.txt", .NET
file system APIs ignore case in the comparison. Test.txt, TEST.TXT, test.TXT, and any other combination of upper-
and lowercase letters will match "test.txt".
Common I/O Tasks
2 minutes to read • Edit Online

The System.IO namespace provides several classes that allow for various actions, such as reading and writing, to
be performed on files, directories, and streams. For more information, see File and Stream I/O.

Common File Tasks


TO DO THIS... SEE THE EXAMPLE IN THIS TOPIC...

Create a text file File.CreateText method

FileInfo.CreateText method

File.Create method

FileInfo.Create method

Write to a text file How to: Write Text to a File

How to: Write a Text File (C++/CLI)

Read from a text file How to: Read Text from a File

Append text to a file How to: Open and Append to a Log File

File.AppendText method

FileInfo.AppendText method

Rename or move a file File.Move method

FileInfo.MoveTo method

Delete a file File.Delete method

FileInfo.Delete method

Copy a file File.Copy method

FileInfo.CopyTo method

Get the size of a file FileInfo.Length property

Get the attributes of a file File.GetAttributes method

Set the attributes of a file File.SetAttributes method

Determine whether a file exists File.Exists method

Read from a binary file How to: Read and Write to a Newly Created Data File
TO DO THIS... SEE THE EXAMPLE IN THIS TOPIC...

Write to a binary file How to: Read and Write to a Newly Created Data File

Retrieve a file name extension Path.GetExtension method

Retrieve the fully qualified path of a file Path.GetFullPath method

Retrieve the file name and extension from a path Path.GetFileName method

Change the extension of a file Path.ChangeExtension method

Common Directory Tasks


TO DO THIS... SEE THE EXAMPLE IN THIS TOPIC...

Access a file in a special folder such as My Documents How to: Write Text to a File

Create a directory Directory.CreateDirectory method

FileInfo.Directory property

Create a subdirectory DirectoryInfo.CreateSubdirectory method

Rename or move a directory Directory.Move method

DirectoryInfo.MoveTo method

Copy a directory How to: Copy Directories

Delete a directory Directory.Delete method

DirectoryInfo.Delete method

See the files and subdirectories in a directory How to: Enumerate Directories and Files

Find the size of a directory System.IO.Directory class

Determine whether a directory exists Directory.Exists method

See also
File and Stream I/O
Composing Streams
Asynchronous File I/O
How to: Copy directories
2 minutes to read • Edit Online

This topic demonstrates how to use I/O classes to synchronously copy the contents of a directory to another
location.
For an example of asynchronous file copy, see Asynchronous file I/O.
This example copies subdirectories by setting the copySubDirs of the DirectoryCopy method to true . The
DirectoryCopy method recursively copies subdirectories by calling itself on each subdirectory until there are no
more to copy.

Example
using System;
using System.IO;

class DirectoryCopyExample
{
static void Main()
{
// Copy from the current directory, include subdirectories.
DirectoryCopy(".", @".\temp", true);
}

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)


{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);

if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}

DirectoryInfo[] dirs = dir.GetDirectories();


// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}

// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}

// If copying subdirectories, copy them and their contents to new location.


if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
}
Imports System.IO

Class DirectoryCopyExample

Shared Sub Main()


' Copy from the current directory, include subdirectories.
DirectoryCopy(".", ".\\temp", True)
End Sub

Private Shared Sub DirectoryCopy( _


ByVal sourceDirName As String, _
ByVal destDirName As String, _
ByVal copySubDirs As Boolean)

' Get the subdirectories for the specified directory.


Dim dir As DirectoryInfo = New DirectoryInfo(sourceDirName)

If Not dir.Exists Then


Throw New DirectoryNotFoundException( _
"Source directory does not exist or could not be found: " _
+ sourceDirName)
End If

Dim dirs As DirectoryInfo() = dir.GetDirectories()


' If the destination directory doesn't exist, create it.
If Not Directory.Exists(destDirName) Then
Directory.CreateDirectory(destDirName)
End If
' Get the files in the directory and copy them to the new location.
Dim files As FileInfo() = dir.GetFiles()
For Each file In files
Dim temppath As String = Path.Combine(destDirName, file.Name)
file.CopyTo(temppath, False)
Next file

' If copying subdirectories, copy them and their contents to new location.
If copySubDirs Then
For Each subdir In dirs
Dim temppath As String = Path.Combine(destDirName, subdir.Name)
DirectoryCopy(subdir.FullName, temppath, copySubDirs)
Next subdir
End If
End Sub
End Class

See also
FileInfo
DirectoryInfo
FileStream
File and stream I/O
Common I/O tasks
Asynchronous file I/O
How to: Enumerate directories and files
4 minutes to read • Edit Online

Enumerable collections provide better performance than arrays when you work with large collections of
directories and files. To enumerate directories and files, use methods that return an enumerable collection of
directory or file names, or their DirectoryInfo, FileInfo, or FileSystemInfo objects.
If you want to search and return only the names of directories or files, use the enumeration methods of the
Directory class. If you want to search and return other properties of directories or files, use the DirectoryInfo and
FileSystemInfo classes.
You can use enumerable collections from these methods as the IEnumerable<T> parameter for constructors of
collection classes like List<T>.
The following table summarizes the methods that return enumerable collections of files and directories:

TO SEARCH AND RETURN USE METHOD

Directory names Directory.EnumerateDirectories

Directory information (DirectoryInfo) DirectoryInfo.EnumerateDirectories

File names Directory.EnumerateFiles

File information (FileInfo) DirectoryInfo.EnumerateFiles

File system entry names Directory.EnumerateFileSystemEntries

File system entry information ( FileSystemInfo) DirectoryInfo.EnumerateFileSystemInfos

Directory and file names Directory.EnumerateFileSystemEntries

NOTE
Although you can immediately enumerate all the files in the subdirectories of a parent directory by using the AllDirectories
option of the optional SearchOption enumeration, UnauthorizedAccessException errors may make the enumeration
incomplete. You can catch these exceptions by first enumerating directories and then enumerating files.

Examples: Use the Directory class


The following example uses the Directory.EnumerateDirectories(String) method to get a list of the top-level
directory names in a specified path.
using System;
using System.Collections.Generic;
using System.IO;

class Program
{
private static void Main(string[] args)
{
try
{
// Set a variable to the My Documents path.
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

List<string> dirs = new List<string>(Directory.EnumerateDirectories(docPath));

foreach (var dir in dirs)


{
Console.WriteLine($"{dir.Substring(dir.LastIndexOf(Path.DirectorySeparatorChar) + 1)}");
}
Console.WriteLine($"{dirs.Count} directories found.");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine(ex.Message);
}
catch (PathTooLongException ex)
{
Console.WriteLine(ex.Message);
}
}
}

Imports System.Collections.Generic
Imports System.IO

Module Module1

Sub Main()
Try
Dim dirPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

Dim dirs As List(Of String) = New List(Of String)(Directory.EnumerateDirectories(dirPath))

For Each folder In dirs


Console.WriteLine($"{dir.Substring(dir.LastIndexOf(Path.DirectorySeparatorChar) + 1)}")
Next
Console.WriteLine($"{dirs.Count} directories found.")
Catch ex As UnauthorizedAccessException
Console.WriteLine(ex.Message)
Catch ex As PathTooLongException
Console.WriteLine(ex.Message)
End Try

End Sub
End Module

The following example uses the Directory.EnumerateFiles(String, String, SearchOption) method to recursively
enumerate all file names in a directory and subdirectories that match a certain pattern. It then reads each line of
each file and displays the lines that contain a specified string, with their filenames and paths.
using System;
using System.IO;
using System.Linq;

class Program
{
static void Main(string[] args)
{
try
{
// Set a variable to the My Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

var files = from file in Directory.EnumerateFiles(docPath, "*.txt", SearchOption.AllDirectories)


from line in File.ReadLines(file)
where line.Contains("Microsoft")
select new
{
File = file,
Line = line
};

foreach (var f in files)


{
Console.WriteLine($"{f.File}\t{f.Line}");
}
Console.WriteLine($"{files.Count().ToString()} files found.");
}
catch (UnauthorizedAccessException uAEx)
{
Console.WriteLine(uAEx.Message);
}
catch (PathTooLongException pathEx)
{
Console.WriteLine(pathEx.Message);
}
}
}

Imports System.IO
Imports System.Xml.Linq

Module Module1

Sub Main()
Try
Dim docPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
Dim files = From chkFile In Directory.EnumerateFiles(docPath, "*.txt",
SearchOption.AllDirectories)
From line In File.ReadLines(chkFile)
Where line.Contains("Microsoft")
Select New With {.curFile = chkFile, .curLine = line}

For Each f In files


Console.WriteLine($"{f.File}\t{f.Line}")
Next
Console.WriteLine($"{files.Count} files found.")
Catch uAEx As UnauthorizedAccessException
Console.WriteLine(uAEx.Message)
Catch pathEx As PathTooLongException
Console.WriteLine(pathEx.Message)
End Try
End Sub
End Module
Examples: Use the DirectoryInfo class
The following example uses the DirectoryInfo.EnumerateDirectories method to list a collection of top-level
directories whose CreationTimeUtc is earlier than a certain DateTime value.

using System;
using System.IO;

namespace EnumDir
{
class Program
{
static void Main(string[] args)
{
// Set a variable to the Documents path.
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

DirectoryInfo dirPrograms = new DirectoryInfo(docPath);


DateTime StartOf2009 = new DateTime(2009, 01, 01);

var dirs = from dir in dirPrograms.EnumerateDirectories()


where dir.CreationTimeUtc > StartOf2009
select new
{
ProgDir = dir,
};

foreach (var di in dirs)


{
Console.WriteLine($"{di.ProgDir.Name}");
}

}
}
}
// </Snippet1>

Imports System.IO

Module Module1

Sub Main()

Dim dirPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)


Dim dirPrograms As New DirectoryInfo(dirPath)
Dim StartOf2009 As New DateTime(2009, 1, 1)

Dim dirs = From dir In dirPrograms.EnumerateDirectories()


Where dir.CreationTimeUtc > StartOf2009

For Each di As DirectoryInfo In dirs


Console.WriteLine("{0}", di.Name)
Next

End Sub

End Module

The following example uses the DirectoryInfo.EnumerateFiles method to list all files whose Length exceeds 10MB.
This example first enumerates the top-level directories, to catch possible unauthorized access exceptions, and then
enumerates the files.

using System;
using System;
using System.IO;

class Program
{
static void Main(string[] args)
{
// Set a variable to the My Documents path.
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

DirectoryInfo diTop = new DirectoryInfo(docPath);

try
{
foreach (var fi in diTop.EnumerateFiles())
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine($"{fi.FullName}\t\t{fi.Length.ToString("NO")}");
}
}
catch (UnauthorizedAccessException unAuthTop)
{
Console.WriteLine($"{unAuthTop.Message}");
}
}

foreach (var di in diTop.EnumerateDirectories("*"))


{
try
{
foreach (var fi in di.EnumerateFiles("*", SearchOption.AllDirectories))
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine($"{fi.FullName}\t\t{fi.Length.ToString("NO")}");
}
}
catch (UnauthorizedAccessException unAuthFile)
{
Console.WriteLine($"unAuthFile: {unAuthFile.Message}");
}
}
}
catch (UnauthorizedAccessException unAuthSubDir)
{
Console.WriteLine($"unAuthSubDir: {unAuthSubDir.Message}");
}
}
}
catch (DirectoryNotFoundException dirNotFound)
{
Console.WriteLine($"{dirNotFound.Message}");
}
catch (UnauthorizedAccessException unAuthDir)
{
Console.WriteLine($"unAuthDir: {unAuthDir.Message}");
}
catch (PathTooLongException longPath)
{
Console.WriteLine($"{longPath.Message}");
}
}
}
}

Imports System.IO

Class Program
Public Shared Sub Main(ByVal args As String())
Dim dirPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
Dim diTop As New DirectoryInfo(dirPath)
Try
For Each fi In diTop.EnumerateFiles()
Try
' Display each file over 10 MB;
If fi.Length > 10000000 Then
Console.WriteLine("{0}" & vbTab & vbTab & "{1}", fi.FullName,
fi.Length.ToString("N0"))
End If
Catch unAuthTop As UnauthorizedAccessException
Console.WriteLine($"{unAuthTop.Message}")
End Try
Next

For Each di In diTop.EnumerateDirectories("*")


Try
For Each fi In di.EnumerateFiles("*", SearchOption.AllDirectories)
Try
' // Display each file over 10 MB;
If fi.Length > 10000000 Then
Console.WriteLine("{0}" & vbTab &
vbTab & "{1}", fi.FullName, fi.Length.ToString("N0"))
End If
Catch unAuthFile As UnauthorizedAccessException
Console.WriteLine($"unAuthFile: {unAuthFile.Message}")
End Try
Next
Catch unAuthSubDir As UnauthorizedAccessException
Console.WriteLine($"unAuthSubDir: {unAuthSubDir.Message}")
End Try
Next
Catch dirNotFound As DirectoryNotFoundException
Console.WriteLine($"{dirNotFound.Message}")
Catch unAuthDir As UnauthorizedAccessException
Console.WriteLine($"unAuthDir: {unAuthDir.Message}")
Catch longPath As PathTooLongException
Console.WriteLine($"{longPath.Message}")
End Try
End Sub
End Class

See also
File and stream I/O
How to: Read and write to a newly created data file
2 minutes to read • Edit Online

The System.IO.BinaryWriter and System.IO.BinaryReader classes are used for writing and reading data other than
character strings. The following example shows how to create an empty file stream, write data to it, and read data
from it.
The example creates a data file called Test.data in the current directory, creates the associated BinaryWriter and
BinaryReader objects, and uses the BinaryWriter object to write the integers 0 through 10 to Test.data, which
leaves the file pointer at the end of the file. The BinaryReader object then sets the file pointer back to the origin
and reads out the specified content.

NOTE
If Test.data already exists in the current directory, an IOException exception is thrown. Use the file mode option
FileMode.Create rather than FileMode.CreateNew to always create a new file without throwing an exception.

Example
using System;
using System.IO;

class MyStream
{
private const string FILE_NAME = "Test.data";

public static void Main()


{
if (File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} already exists!");
return;
}

using (FileStream fs = new FileStream(FILE_NAME, FileMode.CreateNew))


{
using (BinaryWriter w = new BinaryWriter(fs))
{
for (int i = 0; i < 11; i++)
{
w.Write(i);
}
}
}

using (FileStream fs = new FileStream(FILE_NAME, FileMode.Open, FileAccess.Read))


{
using (BinaryReader r = new BinaryReader(fs))
{
for (int i = 0; i < 11; i++)
{
Console.WriteLine(r.ReadInt32());
}
}
}
}
}

// The example creates a file named "Test.data" and writes the integers 0 through 10 to it in binary format.
// It then writes the contents of Test.data to the console with each integer on a separate line.
Imports System.IO

Class MyStream
Private Const FILE_NAME As String = "Test.data"

Public Shared Sub Main()


If File.Exists(FILE_NAME) Then
Console.WriteLine($"{FILE_NAME} already exists!")
Return
End If

Using fs As New FileStream(FILE_NAME, FileMode.CreateNew)


Using w As New BinaryWriter(fs)
For i As Integer = 0 To 10
w.Write(i)
Next
End Using
End Using

Using fs As New FileStream(FILE_NAME, FileMode.Open, FileAccess.Read)


Using r As New BinaryReader(fs)
For i As Integer = 0 To 10
Console.WriteLine(r.ReadInt32())
Next
End Using
End Using
End Sub
End Class

' The example creates a file named "Test.data" and writes the integers 0 through 10 to it in binary format.
' It then writes the contents of Test.data to the console with each integer on a separate line.

See also
BinaryReader
BinaryWriter
FileStream
FileStream.Seek
SeekOrigin
How to: Enumerate directories and files
How to: Open and append to a log file
How to: Read text from a file
How to: Write text to a file
How to: Read characters from a string
How to: Write characters to a string
File and stream I/O
How to: Open and append to a log file
2 minutes to read • Edit Online

StreamWriter and StreamReader write characters to and read characters from streams. The following code
example opens the log.txt file for input, or creates it if it doesn't exist, and appends log information to the end of
the file. The example then writes the contents of the file to standard output for display.
As an alternative to this example, you could store the information as a single string or string array, and use the
File.WriteAllText or File.WriteAllLines method to achieve the same functionality.

NOTE
Visual Basic users may choose to use the methods and properties provided by the Log class or FileSystem class for creating
or writing to log files.

Example
using System;
using System.IO;

class DirAppend
{
public static void Main()
{
using (StreamWriter w = File.AppendText("log.txt"))
{
Log("Test1", w);
Log("Test2", w);
}

using (StreamReader r = File.OpenText("log.txt"))


{
DumpLog(r);
}
}

public static void Log(string logMessage, TextWriter w)


{
w.Write("\r\nLog Entry : ");
w.WriteLine($"{DateTime.Now.ToLongTimeString()} {DateTime.Now.ToLongDateString()}");
w.WriteLine(" :");
w.WriteLine($" :{logMessage}");
w.WriteLine ("-------------------------------");
}

public static void DumpLog(StreamReader r)


{
string line;
while ((line = r.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
// The example creates a file named "log.txt" and writes the following lines to it,
// or appends them to the existing "log.txt" file:

// Log Entry : <current long time string> <current long date string>
// :
// :Test1
// -------------------------------

// Log Entry : <current long time string> <current long date string>
// :
// :Test2
// -------------------------------

// It then writes the contents of "log.txt" to the console.


Imports System.IO

Class DirAppend
Public Shared Sub Main()
Using w As StreamWriter = File.AppendText("log.txt")
Log("Test1", w)
Log("Test2", w)
End Using

Using r As StreamReader = File.OpenText("log.txt")


DumpLog(r)
End Using
End Sub

Public Shared Sub Log(logMessage As String, w As TextWriter)


w.Write(vbCrLf + "Log Entry : ")
w.WriteLine($"{DateTime.Now.ToLongTimeString()} {DateTime.Now.ToLongDateString()}")
w.WriteLine(" :")
w.WriteLine($" :{logMessage}")
w.WriteLine ("-------------------------------")
End Sub

Public Shared Sub DumpLog(r As StreamReader)


Dim line As String
line = r.ReadLine()
While Not (line Is Nothing)
Console.WriteLine(line)
line = r.ReadLine()
End While
End Sub
End Class

' The example creates a file named "log.txt" and writes the following lines to it,
' or appends them to the existing "log.txt" file:

' Log Entry : <current long time string> <current long date string>
' :
' :Test1
' -------------------------------

' Log Entry : <current long time string> <current long date string>
' :
' :Test2
' -------------------------------

' It then writes the contents of "log.txt" to the console.

See also
StreamWriter
StreamReader
File.AppendText
File.OpenText
StreamReader.ReadLine
How to: Enumerate directories and files
How to: Read and write to a newly created data file
How to: Read text from a file
How to: Write text to a file
How to: Read characters from a string
How to: Write characters to a string
File and stream I/O
How to: Write text to a file
5 minutes to read • Edit Online

This topic shows different ways to write text to a file for a .NET app.
The following classes and methods are typically used to write text to a file:
StreamWriter contains methods to write to a file synchronously (Write and WriteLine) or asynchronously
(WriteAsync and WriteLineAsync).
File provides static methods to write text to a file, such as WriteAllLines and WriteAllText, or to append text
to a file, such as AppendAllLines, AppendAllText, and AppendText.
Path is for strings that have file or directory path information. It contains the Combine method and, in
.NET Core 2.1 and later, the Join and TryJoin methods, which allow concatenation of strings to build a file
or directory path.

NOTE
The following examples show only the minimum amount of code needed. A real-world app usually provides more robust
error checking and exception handling.

Example: Synchronously write text with StreamWriter


The following example shows how to use the StreamWriter class to synchronously write text to a new file one line
at a time. Because the StreamWriter object is declared and instantiated in a using statement, the Dispose
method is invoked, which automatically flushes and closes the stream.

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{

// Create a string array with the lines of text


string[] lines = { "First line", "Second line", "Third line" };

// Set a variable to the Documents path.


string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the string array to a new file named "WriteLines.txt".


using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "WriteLines.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
}
}
// The example creates a file named "WriteLines.txt" with the following contents:
// First line
// Second line
// Third line
Imports System.IO

Class WriteText

Public Shared Sub Main()

' Create a string array with the lines of text


Dim lines() As String = {"First line", "Second line", "Third line"}

' Set a variable to the Documents path.


Dim docPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

' Write the string array to a new file named "WriteLines.txt".


Using outputFile As New StreamWriter(Path.Combine(docPath, Convert.ToString("WriteLines.txt")))
For Each line As String In lines
outputFile.WriteLine(line)
Next
End Using

End Sub

End Class

' The example creates a file named "WriteLines.txt" with the following contents:
' First line
' Second line
' Third line

Example: Synchronously append text with StreamWriter


The following example shows how to use the StreamWriter class to synchronously append text to the text file
created in the first example.

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{

// Set a variable to the Documents path.


string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Append text to an existing file named "WriteLines.txt".


using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "WriteLines.txt"), true))
{
outputFile.WriteLine("Fourth Line");
}
}
}
// The example adds the following line to the contents of "WriteLines.txt":
// Fourth Line
Imports System.IO

Class AppendText

Public Shared Sub Main()

' Set a variable to the Documents path.


Dim docPath As String =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

' Append text to an existing file named "WriteLines.txt".


Using outputFile As New StreamWriter(Path.Combine(docPath, Convert.ToString("WriteLines.txt")), True)
outputFile.WriteLine("Fourth Line")
End Using

End Sub

End Class

' The example adds the following line to the contents of "WriteLines.txt":
' Fourth Line

Example: Asynchronously write text with StreamWriter


The following example shows how to asynchronously write text to a new file using the StreamWriter class. To
invoke the WriteAsync method, the method call must be within an async method. The C# example requires C#
7.1 or later, which adds support for the async modifier on the program entry point.

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
// Set a variable to the Documents path.
string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the specified text asynchronously to a new file named "WriteTextAsync.txt".


using (StreamWriter outputFile = new StreamWriter(Path.Combine(docPath, "WriteTextAsync.txt")))
{
await outputFile.WriteAsync("This is a sentence.");
}
}
}
// The example creates a file named "WriteTextAsync.txt" with the following contents:
// This is a sentence.
Imports System.IO

Public Module Example


Public Sub Main()
WriteTextAsync()
End Sub

Async Sub WriteTextAsync()


' Set a variable to the Documents path.
Dim docPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

' Write the text asynchronously to a new file named "WriteTextAsync.txt".


Using outputFile As New StreamWriter(Path.Combine(docPath, Convert.ToString("WriteTextAsync.txt")))
Await outputFile.WriteAsync("This is a sentence.")
End Using
End Sub
End Module

' The example creates a file named "WriteTextAsync.txt" with the following contents:
' This is a sentence.

Example: Write and append text with the File class


The following example shows how to write text to a new file and append new lines of text to the same file using
the File class. The WriteAllText and AppendAllLines methods open and close the file automatically. If the path you
provide to the WriteAllText method already exists, the file is overwritten.

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{
// Create a string with a line of text.
string text = "First line" + Environment.NewLine;

// Set a variable to the Documents path.


string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the text to a new file named "WriteFile.txt".


File.WriteAllText(Path.Combine(docPath, "WriteFile.txt"), text);

// Create a string array with the additional lines of text


string[] lines = { "New line 1", "New line 2" };

// Append new lines of text to the file


File.AppendAllLines(Path.Combine(docPath, "WriteFile.txt"), lines);
}
}
// The example creates a file named "WriteFile.txt" with the contents:
// First line
// And then appends the following contents:
// New line 1
// New line 2
Imports System.IO

Class WriteFile

Public Shared Sub Main()

' Create a string array with the lines of text


Dim text As String = "First line" & Environment.NewLine

' Set a variable to the Documents path.


Dim docPath As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

' Write the text to a new file named "WriteFile.txt".


File.WriteAllText(Path.Combine(docPath, Convert.ToString("WriteFile.txt")), text)

' Create a string array with the additional lines of text


Dim lines() As String = {"New line 1", "New line 2"}

' Append new lines of text to the file


File.AppendAllLines(Path.Combine(docPath, Convert.ToString("WriteFile.txt")), lines)

End Sub

End Class

' The example creates a file named "WriteFile.txt" with the following contents:
' First line
' And then appends the following contents:
' New line 1
' New line 2

See also
StreamWriter
Path
File.CreateText
How to: Enumerate directories and files
How to: Read and write to a newly-created data file
How to: Open and append to a log file
How to: Read text from a file
File and stream I/O
How to: Read text from a file
2 minutes to read • Edit Online

The following examples show how to read text synchronously and asynchronously from a text file using .NET for
desktop apps. In both examples, when you create the instance of the StreamReader class, you provide the relative
or absolute path to the file.

NOTE
These code examples do not apply to developing for Universal Windows (UWP) apps, because the Windows Runtime
provides different stream types for reading and writing to files. For an example that shows how to read text from a file in a
UWP app, see Quickstart: Reading and writing files. For examples that show how to convert between .NET Framework
streams and Windows Runtime streams, see How to: Convert between .NET Framework streams and Windows Runtime
streams.

Example: Synchronous read in a console app


The following example shows a synchronous read operation within a console app. This example opens the text file
using a stream reader, copies the contents to a string, and outputs the string to the console.

IMPORTANT
The example assumes that a file named TestFile.txt already exists in the same folder as the app.

using System;
using System.IO;

class Test
{
public static void Main()
{
try
{ // Open the text file using a stream reader.
using (StreamReader sr = new StreamReader("TestFile.txt"))
{
// Read the stream to a string, and write the string to the console.
String line = sr.ReadToEnd();
Console.WriteLine(line);
}
}
catch (IOException e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}
Imports System.IO

Class Test
Public Shared Sub Main()
Try
' Open the file using a stream reader.
Using sr As New StreamReader("TestFile.txt")
' Read the stream to a string and write the string to the console.
Dim line = sr.ReadToEnd()
Console.WriteLine(line)
End Using
Catch e As IOException
Console.WriteLine("The file could not be read:")
Console.WriteLine(e.Message)
End Try
End Sub
End Class

Example: Asynchronous read in a WPF app


The following example shows an asynchronous read operation in a Windows Presentation Foundation (WPF )
app.

IMPORTANT
The example assumes that a file named TestFile.txt already exists in the same folder as the app.

using System;
using System.IO;
using System.Windows;

namespace TextFiles
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)


{
try
{
using (StreamReader sr = new StreamReader("TestFile.txt"))
{
string line = await sr.ReadToEndAsync();
ResultBlock.Text = line;
}
}
catch (FileNotFoundException ex)
{
ResultBlock.Text = ex.Message;
}
}
}
}
Imports System.IO
Imports System.Windows

''' <summary>
''' Interaction logic for MainWindow.xaml
''' </summary>

Partial Public Class MainWindow


Inherits Window
Public Sub New()
InitializeComponent()
End Sub

Private Async Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs)


Try
Using sr As StreamReader = New StreamReader("TestFile.txt")
Dim line = Await sr.ReadToEndAsync()
ResultBlock.Text = line
End Using
Catch ex As FileNotFoundException
ResultBlock.Text = ex.Message
End Try
End Sub
End Class

See also
StreamReader
File.OpenText
StreamReader.ReadLine
Asynchronous file I/O
How to: Create a directory listing
Quickstart: Reading and writing files
How to: Convert between .NET Framework streams and Windows Runtime streams
How to: Read and write to a newly created data file
How to: Open and append to a log file
How to: Write text to a file
How to: Read characters from a string
How to: Write characters to a string
File and stream I/O
How to: Read characters from a string
2 minutes to read • Edit Online

The following code examples show how to read characters synchronously or asynchronously from a string.

Example: Read characters synchronously


This example reads 13 characters synchronously from a string, stores them in an array, and displays them. The
example then reads the rest of the characters in the string, stores them in the array starting at the sixth element,
and displays the contents of the array.

using System;
using System.IO;

public class CharsFromStr


{
public static void Main()
{
string str = "Some number of characters";
char[] b = new char[str.Length];

using (StringReader sr = new StringReader(str))


{
// Read 13 characters from the string into the array.
sr.Read(b, 0, 13);
Console.WriteLine(b);

// Read the rest of the string starting at the current string position.
// Put in the array starting at the 6th array member.
sr.Read(b, 5, str.Length - 13);
Console.WriteLine(b);
}
}
}
// The example has the following output:
//
// Some number o
// Some f characters
Imports System.IO

Public Class CharsFromStr


Public Shared Sub Main()
Dim str As String = "Some number of characters"
Dim b(str.Length - 1) As Char

Using sr As StringReader = New StringReader(str)


' Read 13 characters from the string into the array.
sr.Read(b, 0, 13)
Console.WriteLine(b)

' Read the rest of the string starting at the current string position.
' Put in the array starting at the 6th array member.
sr.Read(b, 5, str.Length - 13)
Console.WriteLine(b)
End Using
End Sub
End Class
' The example has the following output:
'
' Some number o
' Some f characters

Example: Read characters asynchronously


The next example is the code behind a WPF app. On window load, the example asynchronously reads all
characters from a TextBox control and stores them in an array. It then asynchronously writes each letter or white-
space character to a separate line of a TextBlock control.
using System;
using System.Text;
using System.Windows;
using System.IO;

namespace StringReaderWriter
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Window_Loaded(object sender, RoutedEventArgs e)


{
char[] charsRead = new char[UserInput.Text.Length];
using (StringReader reader = new StringReader(UserInput.Text))
{
await reader.ReadAsync(charsRead, 0, UserInput.Text.Length);
}

StringBuilder reformattedText = new StringBuilder();


using (StringWriter writer = new StringWriter(reformattedText))
{
foreach (char c in charsRead)
{
if (char.IsLetter(c) || char.IsWhiteSpace(c))
{
await writer.WriteLineAsync(char.ToLower(c));
}
}
}
Result.Text = reformattedText.ToString();
}
}
}
Imports System.IO
Imports System.Text

''' <summary>
''' Interaction logic for MainWindow.xaml
''' </summary>

Partial Public Class MainWindow


Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private Async Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Dim charsRead As Char() = New Char(UserInput.Text.Length) {}
Using reader As StringReader = New StringReader(UserInput.Text)
Await reader.ReadAsync(charsRead, 0, UserInput.Text.Length)
End Using

Dim reformattedText As StringBuilder = New StringBuilder()


Using writer As StringWriter = New StringWriter(reformattedText)
For Each c As Char In charsRead
If Char.IsLetter(c) Or Char.IsWhiteSpace(c) Then
Await writer.WriteLineAsync(Char.ToLower(c))
End If
Next
End Using
Result.Text = reformattedText.ToString()
End Sub
End Class

See also
StringReader
StringReader.Read
Asynchronous file I/O
How to: Create a directory listing
How to: Read and write to a newly created data file
How to: Open and append to a log file
How to: Read text from a file
How to: Write text to a file
How to: Write characters to a string
File and stream I/O
How to: Write characters to a string
2 minutes to read • Edit Online

The following code examples write characters synchronously or asynchronously from a character array into a
string.

Example: Write characters synchronously in a console app


The following example uses a StringWriter to write five characters synchronously to a StringBuilder object.

using System;
using System.IO;
using System.Text;

public class CharsToStr


{
public static void Main()
{
StringBuilder sb = new StringBuilder("Start with a string and add from ");
char[] b = { 'c', 'h', 'a', 'r', '.', ' ', 'B', 'u', 't', ' ', 'n', 'o', 't', ' ', 'a', 'l', 'l' };

using (StringWriter sw = new StringWriter(sb))


{
// Write five characters from the array into the StringBuilder.
sw.Write(b, 0, 5);
Console.WriteLine(sb);
}
}
}
// The example has the following output:
//
// Start with a string and add from char.

Imports System.IO
Imports System.Text

Public Class CharsToStr


Public Shared Sub Main()
Dim sb As New StringBuilder("Start with a string and add from ")
Dim b() As Char = { "c", "h", "a", "r", ".", " ", "B", "u", "t", " ", "n", "o", "t", " ", "a", "l",
"l" }

Using sw As StringWriter = New StringWriter(sb)


' Write five characters from the array into the StringBuilder.
sw.Write(b, 0, 5)
Console.WriteLine(sb)
End Using
End Sub
End Class
' The example has the following output:
'
' Start with a string and add from char.
Example: Write characters asynchronously in a WPF app
The next example is the code behind a WPF app. On window load, the example asynchronously reads all
characters from a TextBox control and stores them in an array. It then asynchronously writes each letter or white-
space character to a separate line of a TextBlock control.

using System;
using System.Text;
using System.Windows;
using System.IO;

namespace StringReaderWriter
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Window_Loaded(object sender, RoutedEventArgs e)


{
char[] charsRead = new char[UserInput.Text.Length];
using (StringReader reader = new StringReader(UserInput.Text))
{
await reader.ReadAsync(charsRead, 0, UserInput.Text.Length);
}

StringBuilder reformattedText = new StringBuilder();


using (StringWriter writer = new StringWriter(reformattedText))
{
foreach (char c in charsRead)
{
if (char.IsLetter(c) || char.IsWhiteSpace(c))
{
await writer.WriteLineAsync(char.ToLower(c));
}
}
}
Result.Text = reformattedText.ToString();
}
}
}
Imports System.IO
Imports System.Text

''' <summary>
''' Interaction logic for MainWindow.xaml
''' </summary>

Partial Public Class MainWindow


Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Private Async Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Dim charsRead As Char() = New Char(UserInput.Text.Length) {}
Using reader As StringReader = New StringReader(UserInput.Text)
Await reader.ReadAsync(charsRead, 0, UserInput.Text.Length)
End Using

Dim reformattedText As StringBuilder = New StringBuilder()


Using writer As StringWriter = New StringWriter(reformattedText)
For Each c As Char In charsRead
If Char.IsLetter(c) Or Char.IsWhiteSpace(c) Then
Await writer.WriteLineAsync(Char.ToLower(c))
End If
Next
End Using
Result.Text = reformattedText.ToString()
End Sub
End Class

See also
StringWriter
StringWriter.Write
StringBuilder
File and stream I/O
Asynchronous file I/O
How to: Enumerate directories and files
How to: Read and write to a newly created data file
How to: Open and append to a log file
How to: Read text from a file
How to: Write text to a file
How to: Read characters from a string
How to: Add or remove Access Control List entries
(.NET Framework only)
2 minutes to read • Edit Online

To add or remove Access Control List (ACL ) entries to or from a file or directory, get the FileSecurity or
DirectorySecurity object from the file or directory. Modify the object, and then apply it back to the file or directory.

Add or remove an ACL entry from a file


1. Call the File.GetAccessControl method to get a FileSecurity object that contains the current ACL entries of a
file.
2. Add or remove ACL entries from the FileSecurity object returned from step 1.
3. To apply the changes, pass the FileSecurity object to the File.SetAccessControl method.

Add or remove an ACL entry from a directory


1. Call the Directory.GetAccessControl method to get a DirectorySecurity object that contains the current ACL
entries of a directory.
2. Add or remove ACL entries from the DirectorySecurity object returned from step 1.
3. To apply the changes, pass the DirectorySecurity object to the Directory.SetAccessControl method.

Example
You must use a valid user or group account to run this example. The example uses a File object. Use the same
procedure for the FileInfo, Directory, and DirectoryInfo classes.

using System;
using System.IO;
using System.Security.AccessControl;

namespace FileSystemExample
{
class FileExample
{
public static void Main()
{
try
{
string fileName = "test.xml";

Console.WriteLine("Adding access control entry for "


+ fileName);

// Add the access control entry to the file.


AddFileSecurity(fileName, @"DomainName\AccountName",
FileSystemRights.ReadData, AccessControlType.Allow);

Console.WriteLine("Removing access control entry from "


+ fileName);

// Remove the access control entry from the file.


RemoveFileSecurity(fileName, @"DomainName\AccountName",
FileSystemRights.ReadData, AccessControlType.Allow);
FileSystemRights.ReadData, AccessControlType.Allow);

Console.WriteLine("Done.");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}

// Adds an ACL entry on the specified file for the specified account.
public static void AddFileSecurity(string fileName, string account,
FileSystemRights rights, AccessControlType controlType)
{

// Get a FileSecurity object that represents the


// current security settings.
FileSecurity fSecurity = File.GetAccessControl(fileName);

// Add the FileSystemAccessRule to the security settings.


fSecurity.AddAccessRule(new FileSystemAccessRule(account,
rights, controlType));

// Set the new access settings.


File.SetAccessControl(fileName, fSecurity);

// Removes an ACL entry on the specified file for the specified account.
public static void RemoveFileSecurity(string fileName, string account,
FileSystemRights rights, AccessControlType controlType)
{

// Get a FileSecurity object that represents the


// current security settings.
FileSecurity fSecurity = File.GetAccessControl(fileName);

// Remove the FileSystemAccessRule from the security settings.


fSecurity.RemoveAccessRule(new FileSystemAccessRule(account,
rights, controlType));

// Set the new access settings.


File.SetAccessControl(fileName, fSecurity);

}
}
}

Imports System.IO
Imports System.Security.AccessControl

Module FileExample

Sub Main()
Try
Dim fileName As String = "test.xml"

Console.WriteLine("Adding access control entry for " & fileName)

' Add the access control entry to the file.


AddFileSecurity(fileName, "DomainName\AccountName", _
FileSystemRights.ReadData, AccessControlType.Allow)

Console.WriteLine("Removing access control entry from " & fileName)


' Remove the access control entry from the file.
RemoveFileSecurity(fileName, "DomainName\AccountName", _
FileSystemRights.ReadData, AccessControlType.Allow)

Console.WriteLine("Done.")
Catch e As Exception
Console.WriteLine(e)
End Try

End Sub

' Adds an ACL entry on the specified file for the specified account.
Sub AddFileSecurity(ByVal fileName As String, ByVal account As String, _
ByVal rights As FileSystemRights, ByVal controlType As AccessControlType)

' Get a FileSecurity object that represents the


' current security settings.
Dim fSecurity As FileSecurity = File.GetAccessControl(fileName)

' Add the FileSystemAccessRule to the security settings.


Dim accessRule As FileSystemAccessRule = _
New FileSystemAccessRule(account, rights, controlType)

fSecurity.AddAccessRule(accessRule)

' Set the new access settings.


File.SetAccessControl(fileName, fSecurity)

End Sub

' Removes an ACL entry on the specified file for the specified account.
Sub RemoveFileSecurity(ByVal fileName As String, ByVal account As String, _
ByVal rights As FileSystemRights, ByVal controlType As AccessControlType)

' Get a FileSecurity object that represents the


' current security settings.
Dim fSecurity As FileSecurity = File.GetAccessControl(fileName)

' Remove the FileSystemAccessRule from the security settings.


fSecurity.RemoveAccessRule(New FileSystemAccessRule(account, _
rights, controlType))

' Set the new access settings.


File.SetAccessControl(fileName, fSecurity)

End Sub
End Module
How to: Compress and extract files
5 minutes to read • Edit Online

The System.IO.Compression namespace contains the following types for compressing and decompressing files
and streams. You can also use these types to read and modify the contents of a compressed file.
ZipFile
ZipArchive
ZipArchiveEntry
DeflateStream
GZipStream
The following examples show some of the operations you can perform with compressed files.

Example 1: Create and extract a .zip file


The following example shows how to create and extract a compressed .zip file by using the ZipFile class. The
example compresses the contents of a folder into a new .zip file, and then extracts the zip to a new folder.
To run the sample, create a start folder in your program folder and populate it with files to zip.
If you get the build error "The name 'ZipFile' does not exist in the current context," add a reference to the
System.IO.Compression.FileSystem assembly to your project.

using System;
using System.IO.Compression;

class Program
{
static void Main(string[] args)
{
string startPath = @".\start";
string zipPath = @".\result.zip";
string extractPath = @".\extract";

ZipFile.CreateFromDirectory(startPath, zipPath);

ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
Imports System.IO.Compression

Module Module1

Sub Main()
Dim startPath As String = ".\start"
Dim zipPath As String = ".\result.zip"
Dim extractPath As String = ".\extract"

ZipFile.CreateFromDirectory(startPath, zipPath)

ZipFile.ExtractToDirectory(zipPath, extractPath)
End Sub

End Module

Example 2: Extract specific file extensions


The next example iterates through the contents of an existing .zip file and extracts files that have a .txt extension. It
uses the ZipArchive class to access the zip, and the ZipArchiveEntry class to inspect the individual entries. The
extension method ExtractToFile for the ZipArchiveEntry object is available in the
System.IO.Compression.ZipFileExtensions class.
To run the sample, place a .zip file called result.zip in your program folder. When prompted, provide a folder name
to extract to.
If you get the build error "The name 'ZipFile' does not exist in the current context," add a reference to the
System.IO.Compression.FileSystem assembly to your project.

If you get the error "The type 'ZipArchive' is defined in an assembly that is not referenced," add a reference to the
System.IO.Compression assembly to your project.

IMPORTANT
When unzipping files, you must look for malicious file paths, which can escape out of the directory you unzip into. This is
known as a path traversal attack. The following example demonstrates how to check for malicious file paths and provides a
safe way to unzip.
using System;
using System.IO;
using System.IO.Compression;

class Program
{
static void Main(string[] args)
{
string zipPath = @".\result.zip";

Console.WriteLine("Provide path where to extract the zip file:");


string extractPath = Console.ReadLine();

// Normalizes the path.


extractPath = Path.GetFullPath(extractPath);

// Ensures that the last character on the extraction path


// is the directory separator char.
// Without this, a malicious zip file could try to traverse outside of the expected
// extraction path.
if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
extractPath += Path.DirectorySeparatorChar;

using (ZipArchive archive = ZipFile.OpenRead(zipPath))


{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
{
// Gets the full path to ensure that relative segments are removed.
string destinationPath = Path.GetFullPath(Path.Combine(extractPath, entry.FullName));

// Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
// are case-insensitive.
if (destinationPath.StartsWith(extractPath, StringComparison.Ordinal))
entry.ExtractToFile(destinationPath);
}
}
}
}
}
Imports System.IO
Imports System.IO.Compression

Module Module1

Sub Main()
Dim zipPath As String = ".\result.zip"

Console.WriteLine("Provide path where to extract the zip file:")


Dim extractPath As String = Console.ReadLine()

' Normalizes the path.


extractPath = Path.GetFullPath(extractPath)

' Ensures that the last character on the extraction path


' is the directory separator char.
' Without this, a malicious zip file could try to traverse outside of the expected
' extraction path.
If Not extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) Then
extractPath += Path.DirectorySeparatorChar
End If

Using archive As ZipArchive = ZipFile.OpenRead(zipPath)


For Each entry As ZipArchiveEntry In archive.Entries
If entry.FullName.EndsWith(".txt", StringComparison.OrdinalIgnoreCase) Then

' Gets the full path to ensure that relative segments are removed.
Dim destinationPath As String = Path.GetFullPath(Path.Combine(extractPath,
entry.FullName))

' Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
' are case-insensitive.
If destinationPath.StartsWith(extractPath, StringComparison.Ordinal) Then
entry.ExtractToFile(destinationPath)
End If

End If
Next
End Using
End Sub

End Module

Example 3: Add a file to an existing zip


The following example uses the ZipArchive class to access an existing .zip file, and adds a file to it. The new file gets
compressed when you add it to the existing zip.
using System;
using System.IO;
using System.IO.Compression;

namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
using (FileStream zipToOpen = new FileStream(@"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
}
}
}
}

Imports System.IO
Imports System.IO.Compression

Module Module1

Sub Main()
Using zipToOpen As FileStream = New FileStream("c:\users\exampleuser\release.zip", FileMode.Open)
Using archive As ZipArchive = New ZipArchive(zipToOpen, ZipArchiveMode.Update)
Dim readmeEntry As ZipArchiveEntry = archive.CreateEntry("Readme.txt")
Using writer As StreamWriter = New StreamWriter(readmeEntry.Open())
writer.WriteLine("Information about this package.")
writer.WriteLine("========================")
End Using
End Using
End Using
End Sub

End Module

Example 4: Compress and decompress .gz files


You can also use the GZipStream and DeflateStream classes to compress and decompress data. They use the same
compression algorithm. You can decompress GZipStream objects that are written to a .gz file by using many
common tools. The following example shows how to compress and decompress a directory of files by using the
GZipStream class:
using System;
using System.IO;
using System.IO.Compression;

public class Program


{
private static string directoryPath = @".\temp";
public static void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath);
Compress(directorySelected);

foreach (FileInfo fileToDecompress in directorySelected.GetFiles("*.gz"))


{
Decompress(fileToDecompress);
}
}

public static void Compress(DirectoryInfo directorySelected)


{
foreach (FileInfo fileToCompress in directorySelected.GetFiles())
{
using (FileStream originalFileStream = fileToCompress.OpenRead())
{
if ((File.GetAttributes(fileToCompress.FullName) &
FileAttributes.Hidden) != FileAttributes.Hidden & fileToCompress.Extension != ".gz")
{
using (FileStream compressedFileStream = File.Create(fileToCompress.FullName + ".gz"))
{
using (GZipStream compressionStream = new GZipStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);

}
}
FileInfo info = new FileInfo(directoryPath + Path.DirectorySeparatorChar +
fileToCompress.Name + ".gz");
Console.WriteLine($"Compressed {fileToCompress.Name} from
{fileToCompress.Length.ToString()} to {info.Length.ToString()} bytes.");
}

}
}
}

public static void Decompress(FileInfo fileToDecompress)


{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName = currentFileName.Remove(currentFileName.Length -
fileToDecompress.Extension.Length);

using (FileStream decompressedFileStream = File.Create(newFileName))


{
using (GZipStream decompressionStream = new GZipStream(originalFileStream,
CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine($"Decompressed: {fileToDecompress.Name}");
}
}
}
}
}
Imports System.IO
Imports System.IO.Compression

Module Module1

Private directoryPath As String = ".\temp"


Public Sub Main()
Dim directorySelected As New DirectoryInfo(directoryPath)
Compress(directorySelected)

For Each fileToDecompress As FileInfo In directorySelected.GetFiles("*.gz")


Decompress(fileToDecompress)
Next
End Sub

Public Sub Compress(directorySelected As DirectoryInfo)


For Each fileToCompress As FileInfo In directorySelected.GetFiles()
Using originalFileStream As FileStream = fileToCompress.OpenRead()
If (File.GetAttributes(fileToCompress.FullName) And FileAttributes.Hidden) <>
FileAttributes.Hidden And fileToCompress.Extension <> ".gz" Then
Using compressedFileStream As FileStream = File.Create(fileToCompress.FullName & ".gz")
Using compressionStream As New GZipStream(compressedFileStream,
CompressionMode.Compress)

originalFileStream.CopyTo(compressionStream)
End Using
End Using
Dim info As New FileInfo(directoryPath & Path.DirectorySeparatorChar & fileToCompress.Name
& ".gz")
Console.WriteLine($"Compressed {fileToCompress.Name} from
{fileToCompress.Length.ToString()} to {info.Length.ToString()} bytes.")

End If
End Using
Next
End Sub

Private Sub Decompress(ByVal fileToDecompress As FileInfo)


Using originalFileStream As FileStream = fileToDecompress.OpenRead()
Dim currentFileName As String = fileToDecompress.FullName
Dim newFileName = currentFileName.Remove(currentFileName.Length -
fileToDecompress.Extension.Length)

Using decompressedFileStream As FileStream = File.Create(newFileName)


Using decompressionStream As GZipStream = New GZipStream(originalFileStream,
CompressionMode.Decompress)
decompressionStream.CopyTo(decompressedFileStream)
Console.WriteLine($"Decompressed: {fileToDecompress.Name}")
End Using
End Using
End Using
End Sub
End Module

See also
ZipArchive
ZipFile
ZipArchiveEntry
DeflateStream
GZipStream
File and stream I/O
Compose streams
2 minutes to read • Edit Online

A backing store is a storage medium, such as a disk or memory. Each different backing store implements its own
stream as an implementation of the Stream class.
Each stream type reads and writes bytes from and to its given backing store. Streams that connect to backing
stores are called base streams. Base streams have constructors with the parameters necessary to connect the
stream to the backing store. For example, FileStream has constructors that specify a path parameter, which
specifies how the file will be shared by processes.
The design of the System.IO classes provides simplified stream composition. You can attach base streams to one or
more pass-through streams that provide the functionality you want. You can attach a reader or writer to the end of
the chain, so the preferred types can be read or written easily.
The following code examples create a FileStream around the existing MyFile.txt in order to buffer MyFile.txt. Note
that FileStreams are buffered by default.

IMPORTANT
The examples assume that a file named MyFile.txt already exists in the same folder as the app.

Example: Use StreamReader


The following example creates a StreamReader to read characters from the FileStream, which is passed to the
StreamReader as its constructor argument. StreamReader.ReadLine then reads until StreamReader.Peek finds no
more characters.
using System;
using System.IO;

public class CompBuf


{
private const string FILE_NAME = "MyFile.txt";

public static void Main()


{
if (!File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} does not exist!");
return;
}
FileStream fsIn = new FileStream(FILE_NAME, FileMode.Open,
FileAccess.Read, FileShare.Read);
// Create an instance of StreamReader that can read
// characters from the FileStream.
using (StreamReader sr = new StreamReader(fsIn))
{
string input;
// While not at the end of the file, read lines from the file.
while (sr.Peek() > -1)
{
input = sr.ReadLine();
Console.WriteLine(input);
}
}
}
}

Imports System.IO

Public Class CompBuf


Private Const FILE_NAME As String = "MyFile.txt"

Public Shared Sub Main()


If Not File.Exists(FILE_NAME) Then
Console.WriteLine($"{FILE_NAME} does not exist!")
Return
End If
Dim fsIn As new FileStream(FILE_NAME, FileMode.Open, _
FileAccess.Read, FileShare.Read)
' Create an instance of StreamReader that can read
' characters from the FileStream.
Using sr As New StreamReader(fsIn)
Dim input As String
' While not at the end of the file, read lines from the file.
While sr.Peek() > -1
input = sr.ReadLine()
Console.WriteLine(input)
End While
End Using
End Sub
End Class

Example: Use BinaryReader


The following example creates a BinaryReader to read bytes from the FileStream, which is passed to the
BinaryReader as its constructor argument. ReadByte then reads until PeekChar finds no more bytes.
using System;
using System.IO;

public class ReadBuf


{
private const string FILE_NAME = "MyFile.txt";

public static void Main()


{
if (!File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} does not exist.");
return;
}
FileStream f = new FileStream(FILE_NAME, FileMode.Open,
FileAccess.Read, FileShare.Read);
// Create an instance of BinaryReader that can
// read bytes from the FileStream.
using (BinaryReader br = new BinaryReader(f))
{
byte input;
// While not at the end of the file, read lines from the file.
while (br.PeekChar() > -1 )
{
input = br.ReadByte();
Console.WriteLine(input);
}
}
}
}

Imports System.IO

Public Class ReadBuf


Private Const FILE_NAME As String = "MyFile.txt"

Public Shared Sub Main()


If Not File.Exists(FILE_NAME) Then
Console.WriteLine($"{FILE_NAME} does not exist.")
Return
End If
Dim f As New FileStream(FILE_NAME, FileMode.Open, _
FileAccess.Read, FileShare.Read)
' Create an instance of BinaryReader that can
' read bytes from the FileStream.
Using br As new BinaryReader(f)
Dim input As Byte
' While not at the end of the file, read lines from the file.
While br.PeekChar() > -1
input = br.ReadByte()
Console.WriteLine (input)
End While
End Using
End Sub
End Class

See also
StreamReader
StreamReader.ReadLine
StreamReader.Peek
FileStream
BinaryReader
BinaryReader.ReadByte
BinaryReader.PeekChar
How to: Convert between .NET Framework and
Windows Runtime streams (Windows only)
6 minutes to read • Edit Online

The .NET Framework for UWP apps is a subset of the full .NET Framework. Because of security and other
requirements for UWP apps, you can't use the full set of .NET Framework APIs to open and read files. For more
information, see .NET for UWP apps overview. However, you may want to use .NET Framework APIs for other
stream manipulation operations. To manipulate these streams, you can convert between a .NET Framework
stream type such as MemoryStream or FileStream, and a Windows Runtime stream such as IInputStream,
IOutputStream, or IRandomAccessStream.
The System.IO.WindowsRuntimeStreamExtensions class contains methods that make these conversions easy.
However, underlying differences between .NET Framework and Windows Runtime streams affect the results of
using these methods, as described in the following sections:

Convert from a Windows Runtime to a .NET Framework stream


To convert from a Windows Runtime stream to a .NET Framework stream, use one of the following
System.IO.WindowsRuntimeStreamExtensions methods:
WindowsRuntimeStreamExtensions.AsStream converts a random-access stream in the Windows Runtime
to a managed stream in .NET for UWP apps.
WindowsRuntimeStreamExtensions.AsStreamForWrite converts an output stream in the Windows
Runtime to a managed stream in .NET for UWP apps.
WindowsRuntimeStreamExtensions.AsStreamForRead converts an input stream in the Windows Runtime
to a managed stream in .NET for UWP apps.
The Windows Runtime offers stream types that support reading only, writing only, or reading and writing. These
capabilities are maintained when you convert a Windows Runtime stream to a .NET Framework stream.
Furthermore, if you convert a Windows Runtime stream to a .NET Framework stream and back, you get the
original Windows Runtime instance back.
It’s best practice to use the conversion method that matches the capabilities of the Windows Runtime stream you
want to convert. However, since IRandomAccessStream is readable and writeable (it implements both
IOutputStream and IInputStream), the conversion methods maintain the capabilities of the original stream. For
example, using WindowsRuntimeStreamExtensions.AsStreamForRead to convert an IRandomAccessStream
doesn't limit the converted .NET Framework stream to being readable. It's also writable.

Example: Convert Windows Runtime random-access to .NET


Framework stream
To convert from a Windows Runtime random-access stream to a .NET Framework stream, use the
WindowsRuntimeStreamExtensions.AsStream method.
The following code example prompts you to select a file, opens it with Windows Runtime APIs, and then converts
it to a .NET Framework stream. It reads the stream and outputs it to a text block. You would typically manipulate
the stream with .NET Framework APIs before outputting the results.
To run this example, create a UWP XAML app that contains a text block named TextBlock1 and a button named
Button1 . Associate the button click event with the button1_Click method shown in the example.

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using System.Net.Http;
using Windows.Storage.Pickers;

private async void button1_Click(object sender, RoutedEventArgs e)


{
// Create a file picker.
FileOpenPicker picker = new FileOpenPicker();

// Set properties on the file picker such as start location and the type
// of files to display.
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.ViewMode = PickerViewMode.List;
picker.FileTypeFilter.Add(".txt");

// Show picker enabling user to pick one file.


StorageFile result = await picker.PickSingleFileAsync();

if (result != null)
{
try
{
// Retrieve the stream. This method returns a IRandomAccessStreamWithContentType.
var stream = await result.OpenReadAsync();

// Convert the stream to a .NET stream using AsStream, pass to a


// StreamReader and read the stream.
using (StreamReader sr = new StreamReader(stream.AsStream()))
{
TextBlock1.Text = sr.ReadToEnd();
}
}
catch (Exception ex)
{
TextBlock1.Text = "Error occurred reading the file. " + ex.Message;
}
}
else
{
TextBlock1.Text = "User did not pick a file";
}
}
Imports System.IO
Imports System.Runtime.InteropServices.WindowsRuntime
Imports Windows.UI.Xaml
Imports Windows.UI.Xaml.Controls
Imports Windows.UI.Xaml.Media.Imaging
Imports Windows.Storage
Imports System.Net.Http
Imports Windows.Storage.Pickers

Private Async Sub button1_Click(sender As Object, e As RoutedEventArgs)


' Create a file picker.
Dim picker As New FileOpenPicker()

' Set properties on the file picker such as start location and the type of files to display.
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary
picker.ViewMode = PickerViewMode.List
picker.FileTypeFilter.Add(".txt")

' Show picker that enable user to pick one file.


Dim result As StorageFile = Await picker.PickSingleFileAsync()

If result IsNot Nothing Then


Try
' Retrieve the stream. This method returns a IRandomAccessStreamWithContentType.
Dim stream = Await result.OpenReadAsync()

' Convert the stream to a .NET stream using AsStreamForRead, pass to a


' StreamReader and read the stream.
Using sr As New StreamReader(stream.AsStream())
TextBlock1.Text = sr.ReadToEnd()
End Using
Catch ex As Exception
TextBlock1.Text = "Error occurred reading the file. " + ex.Message
End Try
Else
TextBlock1.Text = "User did not pick a file"
End If
End Sub

Convert from a .NET Framework to a Windows Runtime stream


To convert from a .NET Framework stream to a Windows Runtime stream, use one of the following
System.IO.WindowsRuntimeStreamExtensions methods:
WindowsRuntimeStreamExtensions.AsInputStream converts a managed stream in .NET for UWP apps to
an input stream in the Windows Runtime.
WindowsRuntimeStreamExtensions.AsOutputStream converts a managed stream in .NET for UWP apps to
an output stream in the Windows Runtime.
WindowsRuntimeStreamExtensions.AsRandomAccessStream converts a managed stream in .NET for
UWP apps to a random-access stream that the Windows Runtime can use for reading or writing.
When you convert a .NET Framework stream to a Windows Runtime stream, the capabilities of the converted
stream depend on the original stream. For example, if the original stream supports both reading and writing, and
you call WindowsRuntimeStreamExtensions.AsInputStream to convert the stream, the returned type is an
IRandomAccessStream . IRandomAccessStream implements IInputStream and IOutputStream , and supports reading
and writing.
.NET Framework streams don't support cloning, even after conversion. If you convert a .NET Framework stream
to a Windows Runtime stream and call GetInputStreamAt or GetOutputStreamAt, which call CloneStream, or if
you call CloneStream directly, an exception occurs.
Example: Convert .NET Framework to Windows Runtime random-
access stream
To convert from a .NET Framework stream to a Windows Runtime random-access stream, use the
AsRandomAccessStream method, as shown in the following example:

IMPORTANT
Make sure that the .NET Framework stream you are using supports seeking, or copy it to a stream that does. You can use
the Stream.CanSeek property to determine this.

To run this example, create a UWP XAML app that targets the .NET Framework 4.5.1 and contains a text block
named TextBlock2 and a button named Button2 . Associate the button click event with the button2_Click
method shown in the example.

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Imaging;
using Windows.Storage;
using System.Net.Http;
using Windows.Storage.Pickers;

private async void button2_Click(object sender, RoutedEventArgs e)


{
// Create an HttpClient and access an image as a stream.
var client = new HttpClient();
Stream stream = await client.GetStreamAsync("https://docs.microsoft.com/en-us/dotnet/images/hub/featured-
1.png");
// Create a .NET memory stream.
var memStream = new MemoryStream();
// Convert the stream to the memory stream, because a memory stream supports seeking.
await stream.CopyToAsync(memStream);
// Set the start position.
memStream.Position = 0;
// Create a new bitmap image.
var bitmap = new BitmapImage();
// Set the bitmap source to the stream, which is converted to a IRandomAccessStream.
bitmap.SetSource(memStream.AsRandomAccessStream());
// Set the image control source to the bitmap.
image1.Source = bitmap;
}
Imports System.IO
Imports System.Runtime.InteropServices.WindowsRuntime
Imports Windows.UI.Xaml
Imports Windows.UI.Xaml.Controls
Imports Windows.UI.Xaml.Media.Imaging
Imports Windows.Storage
Imports System.Net.Http
Imports Windows.Storage.Pickers

Private Async Sub button2_Click(sender As Object, e As RoutedEventArgs)

' Create an HttpClient and access an image as a stream.


Dim client = New HttpClient()
Dim stream As Stream = Await client.GetStreamAsync("https://docs.microsoft.com/en-
us/dotnet/images/hub/featured-1.png")
' Create a .NET memory stream.
Dim memStream = New MemoryStream()

' Convert the stream to the memory stream, because a memory stream supports seeking.
Await stream.CopyToAsync(memStream)

' Set the start position.


memStream.Position = 0

' Create a new bitmap image.


Dim bitmap = New BitmapImage()

' Set the bitmap source to the stream, which is converted to a IRandomAccessStream.
bitmap.SetSource(memStream.AsRandomAccessStream())

' Set the image control source to the bitmap.


image1.Source = bitmap
End Sub

See also
Quickstart: Read and write a file (Windows)
.NET for Windows Store apps overview
.NET for Windows Store apps APIs
Asynchronous File I/O
4 minutes to read • Edit Online

Asynchronous operations enable you to perform resource-intensive I/O operations without blocking the main
thread. This performance consideration is particularly important in a Windows 8.x Store app or desktop app
where a time-consuming stream operation can block the UI thread and make your app appear as if it is not
working.
Starting with the .NET Framework 4.5, the I/O types include async methods to simplify asynchronous
operations. An async method contains Async in its name, such as ReadAsync, WriteAsync, CopyToAsync,
FlushAsync, ReadLineAsync, and ReadToEndAsync. These async methods are implemented on stream classes,
such as Stream, FileStream, and MemoryStream, and on classes that are used for reading from or writing to
streams, such TextReader and TextWriter.
In the .NET Framework 4 and earlier versions, you have to use methods such as BeginRead and EndRead to
implement asynchronous I/O operations. These methods are still available in the .NET Framework 4.5 to
support legacy code; however, the async methods help you implement asynchronous I/O operations more easily.
C# and Visual Basic each have two keywords for asynchronous programming:
Async (Visual Basic) or async (C#) modifier, which is used to mark a method that contains an
asynchronous operation.
Await (Visual Basic) or await (C#) operator, which is applied to the result of an async method.

To implement asynchronous I/O operations, use these keywords in conjunction with the async methods, as
shown in the following examples. For more information, see Asynchronous programming with async and await
(C#) or Asynchronous Programming with Async and Await (Visual Basic).
The following example demonstrates how to use two FileStream objects to copy files asynchronously from one
directory to another. Notice that the Click event handler for the Button control is marked with the async
modifier because it calls an asynchronous method.
using System;
using System.Threading.Tasks;
using System.Windows;
using System.IO;

namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Button_Click(object sender, RoutedEventArgs e)


{
string StartDirectory = @"c:\Users\exampleuser\start";
string EndDirectory = @"c:\Users\exampleuser\end";

foreach (string filename in Directory.EnumerateFiles(StartDirectory))


{
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
using (FileStream DestinationStream = File.Create(EndDirectory +
filename.Substring(filename.LastIndexOf('\\'))))
{
await SourceStream.CopyToAsync(DestinationStream);
}
}
}
}
}
}

Imports System.IO

Class MainWindow

Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)


Dim StartDirectory As String = "c:\Users\exampleuser\start"
Dim EndDirectory As String = "c:\Users\exampleuser\end"

For Each filename As String In Directory.EnumerateFiles(StartDirectory)


Using SourceStream As FileStream = File.Open(filename, FileMode.Open)
Using DestinationStream As FileStream = File.Create(EndDirectory +
filename.Substring(filename.LastIndexOf("\"c)))
Await SourceStream.CopyToAsync(DestinationStream)
End Using

End Using
Next
End Sub

End Class

The next example is similar to the previous one but uses StreamReader and StreamWriter objects to read and
write the contents of a text file asynchronously.
private async void Button_Click(object sender, RoutedEventArgs e)
{
string UserDirectory = @"c:\Users\exampleuser\";

using (StreamReader SourceReader = File.OpenText(UserDirectory + "BigFile.txt"))


{
using (StreamWriter DestinationWriter = File.CreateText(UserDirectory + "CopiedFile.txt"))
{
await CopyFilesAsync(SourceReader, DestinationWriter);
}
}
}

public async Task CopyFilesAsync(StreamReader Source, StreamWriter Destination)


{
char[] buffer = new char[0x1000];
int numRead;
while ((numRead = await Source.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
await Destination.WriteAsync(buffer, 0, numRead);
}
}

Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)


Dim UserDirectory As String = "c:\Users\exampleuser\"

Using SourceReader As StreamReader = File.OpenText(UserDirectory + "BigFile.txt")


Using DestinationWriter As StreamWriter = File.CreateText(UserDirectory + "CopiedFile.txt")
Await CopyFilesAsync(SourceReader, DestinationWriter)
End Using
End Using
End Sub

Public Async Function CopyFilesAsync(Source As StreamReader, Destination As StreamWriter) As Task


Dim buffer(4095) As Char
Dim numRead As Integer

numRead = Await Source.ReadAsync(buffer, 0, buffer.Length)


Do While numRead <> 0
Await Destination.WriteAsync(buffer, 0, numRead)
numRead = Await Source.ReadAsync(buffer, 0, buffer.Length)
Loop

End Function

The next example shows the code-behind file and the XAML file that are used to open a file as a Stream in a
Windows 8.x Store app, and read its contents by using an instance of the StreamReader class. It uses
asynchronous methods to open the file as a stream and to read its contents.
using System;
using System.IO;
using System.Text;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace ExampleApplication
{
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
}

private async void Button_Click_1(object sender, RoutedEventArgs e)


{
StringBuilder contents = new StringBuilder();
string nextLine;
int lineCounter = 1;

var openPicker = new FileOpenPicker();


openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".txt");
StorageFile selectedFile = await openPicker.PickSingleFileAsync();

using (StreamReader reader = new StreamReader(await selectedFile.OpenStreamForReadAsync()))


{
while ((nextLine = await reader.ReadLineAsync()) != null)
{
contents.AppendFormat("{0}. ", lineCounter);
contents.Append(nextLine);
contents.AppendLine();
lineCounter++;
if (lineCounter > 3)
{
contents.AppendLine("Only first 3 lines shown.");
break;
}
}
}
DisplayContentsBlock.Text = contents.ToString();
}
}
}
Imports System.Text
Imports System.IO
Imports Windows.Storage.Pickers
Imports Windows.Storage

NotInheritable Public Class BlankPage


Inherits Page

Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)


Dim contents As StringBuilder = New StringBuilder()
Dim nextLine As String
Dim lineCounter As Integer = 1

Dim openPicker = New FileOpenPicker()


openPicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary

openPicker.FileTypeFilter.Add(".txt")
Dim selectedFile As StorageFile = Await openPicker.PickSingleFileAsync()

Using reader As StreamReader = New StreamReader(Await selectedFile.OpenStreamForReadAsync())


nextLine = Await reader.ReadLineAsync()
While (nextLine <> Nothing)
contents.AppendFormat("{0}. ", lineCounter)
contents.Append(nextLine)
contents.AppendLine()
lineCounter = lineCounter + 1
If (lineCounter > 3) Then
contents.AppendLine("Only first 3 lines shown.")
Exit While
End If
nextLine = Await reader.ReadLineAsync()
End While
End Using
DisplayContentsBlock.Text = contents.ToString()
End Sub
End Class

<Page
x:Class="ExampleApplication.BlankPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ExampleApplication"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<StackPanel Background="{StaticResource ApplicationPageBackgroundBrush}" VerticalAlignment="Center"


HorizontalAlignment="Center">
<TextBlock Text="Display lines from a file."></TextBlock>
<Button Content="Load File" Click="Button_Click_1"></Button>
<TextBlock Name="DisplayContentsBlock"></TextBlock>
</StackPanel>
</Page>

See also
Stream
File and Stream I/O
Asynchronous programming with async and await (C#)
Asynchronous Programming with Async and Await (Visual Basic)
Handling I/O errors in .NET
5 minutes to read • Edit Online

In addition to the exceptions that can be thrown in any method call (such as an OutOfMemoryException when a
system is stressed or an NullReferenceException due to programmer error), .NET file system methods can throw
the following exceptions:
System.IO.IOException, the base class of all System.IO exception types. It is thrown for errors whose return
codes from the operating system don't directly map to any other exception type.
System.IO.FileNotFoundException.
System.IO.DirectoryNotFoundException.
DriveNotFoundException.
System.IO.PathTooLongException.
System.OperationCanceledException.
System.UnauthorizedAccessException.
System.ArgumentException, which is thrown for invalid path characters on .NET Framework and on .NET Core
2.0 and previous versions.
System.NotSupportedException, which is thrown for invalid colons in .NET Framework.
System.Security.SecurityException, which is thrown for applications running in limited trust that lack the
necessary permissions on .NET Framework only. (Full trust is the default on .NET Framework.)

Mapping error codes to exceptions


Because the file system is an operating system resource, I/O methods in both .NET Core and .NET Framework
wrap calls to the underlying operating system. When an I/O error occurs in code executed by the operating system,
the operating system returns error information to the .NET I/O method. The method then translates the error
information, typically in the form of an error code, into a .NET exception type. In most cases, it does this by directly
translating the error code into its corresponding exception type; it does not perform any special mapping of the
error based on the context of the method call.
For example, on the Windows operating system, a method call that returns an error code of ERROR_FILE_NOT_FOUND
(or 0x02) maps to a FileNotFoundException, and an error code of ERROR_PATH_NOT_FOUND (or 0x03) maps to a
DirectoryNotFoundException.
However, the precise conditions under which the operating system returns particular error codes is often
undocumented or poorly documented. As a result, unexpected exceptions can occur. For example, because you are
working with a directory rather than a file, you would expect that providing an invalid directory path to the
DirectoryInfo.DirectoryInfo constructor throws a DirectoryNotFoundException. However, it may also throw a
FileNotFoundException.

Exception handling in I/O operations


Because of this reliance on the operating system, identical exception conditions (such as the directory not found
error in our example) can result in an I/O method throwing any one of the entire class of I/O exceptions. This
means that, when calling I/O APIs, your code should be prepared to handle most or all of these exceptions, as
shown in the following table:
EXCEPTION TYPE .NET CORE .NET FRAMEWORK

IOException Yes Yes

FileNotFoundException Yes Yes

DirectoryNotFoundException Yes Yes

DriveNotFoundException Yes Yes

PathTooLongException Yes Yes

OperationCanceledException Yes Yes

UnauthorizedAccessException Yes Yes

ArgumentException .NET Core 2.0 and earlier Yes

NotSupportedException No Yes

SecurityException No Limited trust only

Handling IOException
As the base class for exceptions in the System.IO namespace, IOException is also thrown for any error code that
does not map to a predefined exception type. This means that it can be thrown by any I/O operation.

IMPORTANT
Because IOException is the base class of the other exception types in the System.IO namespace, you should handle in a
catch block after you've handled the other I/O-related exceptions.

In addition, starting with .NET Core 2.1, validation checks for path correctness (for example, to ensure that invalid
characters are not present in a path) have been removed, and the runtime throws an exception mapped from an
operating system error code rather than from its own validation code. The most likely exception to be thrown in
this case is an IOException, although any other exception type could also be thrown.
Note that, in your exception handling code, you should always handle the IOException last. Otherwise, because it is
the base class of all other IO exceptions, the catch blocks of derived classes will not be evaluated.
In the case of an IOException, you can get additional error information from the IOException.HResult property. To
convert the HResult value to a Win32 error code, you strip out the upper 16 bits of the 32-bit value. The following
table lists error codes that may be wrapped in an IOException.

HRESULT CONSTANT DESCRIPTION

ERROR_SHARING_VIOLATION 32 The file name is missing, or the file or


directory is in use.

ERROR_FILE_EXISTS 80 The file already exists.

ERROR_INVALID_PARAMETER 87 An argument supplied to the method is


invalid.
HRESULT CONSTANT DESCRIPTION

ERROR_ALREADY_EXISTS 183 The file or directory already exists.

You can handle these using a When clause in a catch statement, as the following example shows.

using System;
using System.IO;
using System.Text;

class Program
{
static void Main()
{
var sw = OpenStream(@".\textfile.txt");
if (sw is null)
return;
sw.WriteLine("This is the first line.");
sw.WriteLine("This is the second line.");
sw.Close();
}

static StreamWriter OpenStream(string path)


{
if (path is null) {
Console.WriteLine("You did not supply a file path.");
return null;
}

try {
var fs = new FileStream(path, FileMode.CreateNew);
return new StreamWriter(fs);
}
catch (FileNotFoundException) {
Console.WriteLine("The file or directory cannot be found.");
}
catch (DirectoryNotFoundException) {
Console.WriteLine("The file or directory cannot be found.");
}
catch (DriveNotFoundException) {
Console.WriteLine("The drive specified in 'path' is invalid.");
}
catch (PathTooLongException) {
Console.WriteLine("'path' exceeds the maxium supported path length.");
}
catch (UnauthorizedAccessException) {
Console.WriteLine("You do not have permission to create this file.");
}
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
Console.WriteLine("There is a sharing violation.");
}
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 80) {
Console.WriteLine("The file already exists.");
}
catch (IOException e) {
Console.WriteLine($"An exception occurred:\nError code: " +
$"{e.HResult & 0x0000FFFF}\nMessage: {e.Message}");
}
return null;
}
}
Imports System.IO

Module Program
Sub Main(args As String())
Dim sw = OpenStream(".\textfile.txt")
If sw Is Nothing Then Return

sw.WriteLine("This is the first line.")


sw.WriteLine("This is the second line.")
sw.Close()
End Sub

Function OpenStream(path As String) As StreamWriter


If path Is Nothing Then
Console.WriteLine("You did not supply a file path.")
Return Nothing
End If

Try
Dim fs As New FileStream(path, FileMode.CreateNew)
Return New StreamWriter(fs)
Catch e As FileNotFoundException
Console.WriteLine("The file or directory cannot be found.")
Catch e As DirectoryNotFoundException
Console.WriteLine("The file or directory cannot be found.")
Catch e As DriveNotFoundException
Console.WriteLine("The drive specified in 'path' is invalid.")
Catch e As PathTooLongException
Console.WriteLine("'path' exceeds the maxium supported path length.")
Catch e As UnauthorizedAccessException
Console.WriteLine("You do not have permission to create this file.")
Catch e As IOException When (e.HResult And &h0000FFFF) = 32
Console.WriteLine("There is a sharing violation.")
Catch e As IOException When (e.HResult And &h0000FFFF) = 80
Console.WriteLine("The file already exists.")
Catch e As IOException
Console.WriteLine($"An exception occurred:{vbCrLf}Error code: " +
$"{e.HResult And &h0000FFFF}{vbCrLf}Message: {e.Message}")
End Try
Return Nothing
End Function
End Module

See also
Handling and throwing exceptions in .NET
Exception handling (Task Parallel Library)
Best practices for exceptions
How to use specific exceptions in a catch block
Isolated Storage
11 minutes to read • Edit Online

For desktop apps, isolated storage is a data storage mechanism that provides isolation and safety by defining
standardized ways of associating code with saved data. Standardization provides other benefits as well.
Administrators can use tools designed to manipulate isolated storage to configure file storage space, set security
policies, and delete unused data. With isolated storage, your code no longer needs unique paths to specify safe
locations in the file system, and data is protected from other applications that only have isolated storage access.
Hard-coded information that indicates where an application's storage area is located is unnecessary.

IMPORTANT
Isolated storage is not available for Windows 8.x Store apps. Instead, use the application data classes in the
Windows.Storage namespaces included in the Windows Runtime API to store local data and files. For more information,
see Application data in the Windows Dev Center.

This topic contains the following sections:


Data Compartments and Stores
Quotas for Isolated Storage
Secure Access
Allowed Usage and Security Risks
Isolated Storage Locations
Creating, Enumerating, and Deleting Isolated Storage
Scenarios for Isolated Storage
Related Topics
Reference

Data Compartments and Stores


When an application stores data in a file, the file name and storage location must be carefully chosen to minimize
the possibility that the storage location will be known to another application and, therefore, will be vulnerable to
corruption. Without a standard system in place to manage these problems, developing ad hoc techniques that
minimize storage conflicts can be complex, and the results can be unreliable.
With isolated storage, data is always isolated by user and by assembly. Credentials such as the origin or the
strong name of the assembly determine assembly identity. Data can also be isolated by application domain, using
similar credentials.
When you use isolated storage, your application saves data to a unique data compartment that is associated with
some aspect of the code's identity, such as its publisher or signature. The data compartment is an abstraction, not
a specific storage location; it consists of one or more isolated storage files, called stores, which contain the actual
directory locations where data is stored. For example, an application might have a data compartment associated
with it, and a directory in the file system would implement the store that actually preserves the data for that
application. The data saved in the store can be any kind of data, from user preference information to application
state. For the developer, the location of the data compartment is transparent. Stores usually reside on the client,
but a server application could use isolated stores to store information by impersonating the user on whose
behalf it is functioning. Isolated storage can also store information on a server with a user's roaming profile so
that the information will travel with the roaming user.

Quotas for Isolated Storage


A quota is a limit on the amount of isolated storage that can be used. The quota includes bytes of file space as
well as the overhead associated with the directory and other information in the store. Isolated storage uses
permission quotas, which are storage limits that are set by using IsolatedStoragePermission objects. If you try to
write data that exceeds the quota, an IsolatedStorageException exception is thrown. Security policy, which can be
modified using the .NET Framework Configuration Tool (Mscorcfg.msc), determines which permissions are
granted to code. Code that has been granted IsolatedStoragePermission is restricted to using no more storage
than the UserQuota property allows. However, because code can bypass permission quotas by presenting
different user identities, permission quotas serve as guidelines for how code should behave rather than as a firm
limit on code behavior.
Quotas are not enforced on roaming stores. Because of this, a slightly higher level of permission is required for
code to use them. The enumeration values AssemblyIsolationByRoamingUser and
DomainIsolationByRoamingUser specify a permission to use isolated storage for a roaming user.

Secure Access
Using isolated storage enables partially trusted applications to store data in a manner that is controlled by the
computer's security policy. This is especially useful for downloaded components that a user might want to run
cautiously. Security policy rarely grants this kind of code permission when you access the file system by using
standard I/O mechanisms. However, by default, code running from the local computer, a local network, or the
Internet is granted the right to use isolated storage.
Administrators can limit how much isolated storage an application or a user has available, based on an
appropriate trust level. In addition, administrators can remove a user's persisted data completely. To create or
access isolated storage, code must be granted the appropriate IsolatedStorageFilePermission permission.
To access isolated storage, code must have all necessary native platform operating system rights. The access
control lists (ACLs) that control which users have the rights to use the file system must be satisfied. .NET
Framework applications already have operating system rights to access isolated storage unless they perform
(platform-specific) impersonation. In this case, the application is responsible for ensuring that the impersonated
user identity has the proper operating system rights to access isolated storage. This access provides a convenient
way for code that is run or downloaded from the web to read and write to a storage area related to a particular
user.
To control access to isolated storage, the common language runtime uses IsolatedStorageFilePermission objects.
Each object has properties that specify the following values:
Allowed usage, which indicates the type of access that is allowed. The values are members of the
IsolatedStorageContainment enumeration. For more information about these values, see the table in the
next section.
Storage quota, as discussed in the preceding section.
The runtime demands IsolatedStorageFilePermission permission when code first attempts to open a store. It
decides whether to grant this permission, based on how much the code is trusted. If the permission is granted,
the allowed usage and storage quota values are determined by security policy and by the code's request for
IsolatedStorageFilePermission. Security policy is set by using the .NET Framework Configuration Tool
(Mscorcfg.msc). All callers in the call stack are checked to ensure that each caller has at least the appropriate
allowed usage. The runtime also checks the quota imposed on the code that opened or created the store in which
the file is to be saved. If these conditions are satisfied, permission is granted. The quota is checked again every
time a file is written to the store.
Application code is not required to request permission because the common language runtime will grant
whatever IsolatedStorageFilePermission is appropriate based on security policy. However, there are good
reasons to request specific permissions that your application needs, including IsolatedStorageFilePermission.

Allowed Usage and Security Risks


The allowed usage specified by IsolatedStorageFilePermission determines the degree to which code will be
allowed to create and use isolated storage. The following table shows how the allowed usage specified in the
permission corresponds to types of isolation and summarizes the security risks associated with each allowed
usage.

ALLOWED USAGE ISOLATION TYPES SECURITY IMPACT

None No isolated storage use is allowed. There is no security impact.

DomainIsolationByUser Isolation by user, domain, and This permission level leaves resources
assembly. Each assembly has a open to unauthorized overuse,
separate substore within the domain. although enforced quotas make it
Stores that use this permission are also more difficult. This is called a denial of
implicitly isolated by computer. service attack.

DomainIsolationByRoamingUser Same as DomainIsolationByUser , but Because quotas must be disabled,


store is saved to a location that will storage resources are more vulnerable
roam if roaming user profiles are to a denial of service attack.
enabled and quotas are not enforced.

AssemblyIsolationByUser Isolation by user and assembly. Stores Quotas are enforced at this level to
that use this permission are also help prevent a denial of service attack.
implicitly isolated by computer. The same assembly in another domain
can access this store, opening the
possibility that information could be
leaked between applications.

AssemblyIsolationByRoamingUser Same as AssemblyIsolationByUser , Same as in AssemblyIsolationByUser


but store is saved to a location that will , but without quotas, the risk of a
roam if roaming user profiles are denial of service attack increases.
enabled and quotas are not enforced.

AdministerIsolatedStorageByUser Isolation by user. Typically, only Access with this permission allows code
administrative or debugging tools use to view or delete any of a user's
this level of permission. isolated storage files or directories
(regardless of assembly isolation). Risks
include, but are not limited to, leaking
information and data loss.

UnrestrictedIsolatedStorage Isolation by all users, domains, and This permission creates the potential
assemblies. Typically, only for a total compromise of all isolated
administrative or debugging tools use stores for all users.
this level of permission.

Isolated Storage Locations


Sometimes it is helpful to verify a change to isolated storage by using the file system of the operating system.
You might also want to know the location of isolated storage files. This location is different depending on the
operating system. The following table shows the root locations where isolated storage is created on a few
common operating systems. Look for Microsoft\IsolatedStorage directories under this root location. You must
change folder settings to show hidden files and folders in order to see isolated storage in the file system.

OPERATING SYSTEM LOCATION IN FILE SYSTEM

Windows 2000, Windows XP, Windows Server 2003 (upgrade Roaming-enabled stores =
from Windows NT 4.0)
<SYSTEMROOT>\Profiles\<user>\Application Data

Nonroaming stores =

<SYSTEMROOT>\Profiles\<user>\Local Settings\Application
Data

Windows 2000 - clean installation (and upgrades from Roaming-enabled stores =


Windows 98 and Windows NT 3.51)
<SYSTEMDRIVE>\Documents and Settings\
<user>\Application Data

Nonroaming stores =

<SYSTEMDRIVE>\Documents and Settings\<user>\Local


Settings\Application Data

Windows XP, Windows Server 2003 - clean installation (and Roaming-enabled stores =
upgrades from Windows 2000 and Windows 98)
<SYSTEMDRIVE>\Documents and Settings\
<user>\Application Data

Nonroaming stores =

<SYSTEMDRIVE>\Documents and Settings\<user>\Local


Settings\Application Data

Windows 8, Windows 7, Windows Server 2008, Windows Roaming-enabled stores =


Vista
<SYSTEMDRIVE>\Users\<user>\AppData\Roaming

Nonroaming stores =

<SYSTEMDRIVE>\Users\<user>\AppData\Local

Creating, Enumerating, and Deleting Isolated Storage


The .NET Framework provides three classes in the System.IO.IsolatedStorage namespace to help you perform
tasks that involve isolated storage:
IsolatedStorageFile, derives from System.IO.IsolatedStorage.IsolatedStorage and provides basic
management of stored assembly and application files. An instance of the IsolatedStorageFile class
represents a single store located in the file system.
IsolatedStorageFileStream derives from System.IO.FileStream and provides access to the files in a store.
IsolatedStorageScope is an enumeration that enables you to create and select a store with the appropriate
isolation type.
The isolated storage classes enable you to create, enumerate, and delete isolated storage. The methods for
performing these tasks are available through the IsolatedStorageFile object. Some operations require you to
have the IsolatedStorageFilePermission permission that represents the right to administer isolated storage; you
might also need to have operating system rights to access the file or directory.
For a series of examples that demonstrate common isolated storage tasks, see the how -to topics listed in Related
Topics.

Scenarios for Isolated Storage


Isolated storage is useful in many situations, including these four scenarios:
Downloaded controls. Managed code controls downloaded from the Internet are not allowed to write to
the hard drive through normal I/O classes, but they can use isolated storage to persist users' settings and
application states.
Shared component storage. Components that are shared between applications can use isolated storage to
provide controlled access to data stores.
Server storage. Server applications can use isolated storage to provide individual stores for a large
number of users making requests to the application. Because isolated storage is always segregated by
user, the server must impersonate the user making the request. In this case, data is isolated based on the
identity of the principal, which is the same identity the application uses to distinguish between its users.
Roaming. Applications can also use isolated storage with roaming user profiles. This allows a user's
isolated stores to roam with the profile.
You should not use isolated storage in the following situations:
To store high-value secrets, such as unencrypted keys or passwords, because isolated storage is not
protected from highly trusted code, from unmanaged code, or from trusted users of the computer.
To store code.
To store configuration and deployment settings, which administrators control. (User preferences are not
considered to be configuration settings because administrators do not control them.)
Many applications use a database to store and isolate data, in which case one or more rows in a database might
represent storage for a specific user. You might choose to use isolated storage instead of a database when the
number of users is small, when the overhead of using a database is significant, or when no database facility
exists. Also, when the application requires storage that is more flexible and complex than what a row in a
database provides, isolated storage can provide a viable alternative.

Related Topics
TITLE DESCRIPTION

Types of Isolation Describes the different types of isolation.

How to: Obtain Stores for Isolated Storage Provides an example of using the IsolatedStorageFile class to
obtain a store isolated by user and assembly.

How to: Enumerate Stores for Isolated Storage Shows how to use the IsolatedStorageFile.GetEnumerator
method to calculate the size of all isolated storage for the
user.

How to: Delete Stores in Isolated Storage Shows how to use the IsolatedStorageFile.Remove method in
two different ways to delete isolated stores.
TITLE DESCRIPTION

How to: Anticipate Out-of-Space Conditions with Isolated Shows how to measure the remaining space in an isolated
Storage store.

How to: Create Files and Directories in Isolated Storage Provides some examples of creating files and directories in an
isolated store.

How to: Find Existing Files and Directories in Isolated Storage Demonstrates how to read the directory structure and files in
isolated storage.

How to: Read and Write to Files in Isolated Storage Provides an example of writing a string to an isolated storage
file and reading it back.

How to: Delete Files and Directories in Isolated Storage Demonstrates how to delete isolated storage files and
directories.

File and Stream I/O Explains how you can perform synchronous and
asynchronous file and data stream access.

Reference
System.IO.IsolatedStorage.IsolatedStorage
System.IO.IsolatedStorage.IsolatedStorageFile
System.IO.IsolatedStorage.IsolatedStorageFileStream
System.IO.IsolatedStorage.IsolatedStorageScope
Types of Isolation
5 minutes to read • Edit Online

Access to isolated storage is always restricted to the user who created it. To implement this type of isolation, the
common language runtime uses the same notion of user identity that the operating system recognizes, which is
the identity associated with the process in which the code is running when the store is opened. This identity is an
authenticated user identity, but impersonation can cause the identity of the current user to change dynamically.
Access to isolated storage is also restricted according to the identity associated with the application's domain and
assembly, or with the assembly alone. The runtime obtains these identities in the following ways:
Domain identity represents the evidence of the application, which in the case of a web application might be
the full URL. For shell-hosted code, the domain identity might be based on the application directory path.
For example, if the executable runs from the path C:\Office\MyApp.exe, the domain identity would be
C:\Office\MyApp.exe.
Assembly identity is the evidence of the assembly. This might come from a cryptographic digital signature,
which can be the assembly's strong name, the software publisher of the assembly, or its URL identity. If an
assembly has both a strong name and a software publisher identity, then the software publisher identity is
used. If the assembly comes from the Internet and is unsigned, the URL identity is used. For more
information about assemblies and strong names, see Programming with Assemblies.
Roaming stores move with a user that has a roaming user profile. Files are written to a network directory
and are downloaded to any computer the user logs into. For more information about roaming user profiles,
see IsolatedStorageScope.Roaming.
By combining the concepts of user, domain, and assembly identity, isolated storage can isolate data in the
following ways, each of which has its own usage scenarios:
Isolation by user and assembly
Isolation by user, domain, and assembly
Either of these isolations can be combined with a roaming user profile. For more information, see the section
Isolated Storage and Roaming.
The following illustration demonstrates how stores are isolated in different scopes:

Note that except for roaming stores, isolated storage is always implicitly isolated by computer because it uses the
storage facilities that are local to a given computer.
IMPORTANT
Isolated storage is not available for Windows 8.x Store apps. Instead, use the application data classes in the
Windows.Storage namespaces included in the Windows Runtime API to store local data and files. For more information, see
Application data in the Windows Dev Center.

Isolation by User and Assembly


When the assembly that uses the data store needs to be accessible from any application's domain, isolation by
user and assembly is appropriate. Typically, in this situation, isolated storage is used to store data that applies
across multiple applications and is not tied to any particular application, such as the user's name or license
information. To access storage isolated by user and assembly, code must be trusted to transfer information
between applications. Typically, isolation by user and assembly is allowed on intranets but not on the Internet.
Calling the static IsolatedStorageFile.GetStore method and passing in a user and an assembly
IsolatedStorageScope returns storage with this kind of isolation.
The following code example retrieves a store that is isolated by user and assembly. The store can be accessed
through the isoFile object.

IsolatedStorageFile^ isoFile =
IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Assembly, (Type^)nullptr, (Type^)nullptr);

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Assembly, Nothing, Nothing)

For an example that uses the evidence parameters, see GetStore(IsolatedStorageScope, Evidence, Type, Evidence,
Type).
The GetUserStoreForAssembly method is available as a shortcut, as shown in the following code example. This
shortcut cannot be used to open stores that are capable of roaming; use GetStore in such cases.

IsolatedStorageFile^ isoFile = IsolatedStorageFile::GetUserStoreForAssembly();

IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForAssembly();

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetUserStoreForAssembly()

Isolation by User, Domain, and Assembly


If your application uses a third-party assembly that requires a private data store, you can use isolated storage to
store the private data. Isolation by user, domain, and assembly ensures that only code in a given assembly can
access the data, and only when the assembly is used by the application that was running when the assembly
created the store, and only when the user for whom the store was created runs the application. Isolation by user,
domain, and assembly keeps the third-party assembly from leaking data to other applications. This isolation type
should be your default choice if you know that you want to use isolated storage but are not sure which type of
isolation to use. Calling the static GetStore method of IsolatedStorageFile and passing in a user, domain, and
assembly IsolatedStorageScope returns storage with this kind of isolation.
The following code example retrieves a store isolated by user, domain, and assembly. The store can be accessed
through the isoFile object.

IsolatedStorageFile^ isoFile =
IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Domain |
IsolatedStorageScope::Assembly, (Type^)nullptr, (Type^)nullptr);

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain |
IsolatedStorageScope.Assembly, null, null);

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Domain Or _
IsolatedStorageScope.Assembly, Nothing, Nothing)

Another method is available as a shortcut, as shown in the following code example. This shortcut cannot be used
to open stores that are capable of roaming; use GetStore in such cases.

IsolatedStorageFile^ isoFile = IsolatedStorageFile::GetUserStoreForDomain();

IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForDomain();

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetUserStoreForDomain()

Isolated Storage and Roaming


Roaming user profiles are a Windows feature that enables a user to set up an identity on a network and use that
identity to log into any network computer, carrying over all personalized settings. An assembly that uses isolated
storage can specify that the user's isolated storage should move with the roaming user profile. Roaming can be
used in conjunction with isolation by user and assembly or with isolation by user, domain, and assembly. If a
roaming scope is not used, stores will not roam even if a roaming user profile is used.
The following code example retrieves a roaming store isolated by user and assembly. The store can be accessed
through the isoFile object.

IsolatedStorageFile^ isoFile =
IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Assembly |
IsolatedStorageScope::Roaming, (Type^)nullptr, (Type^)nullptr);
IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly |
IsolatedStorageScope.Roaming, null, null);

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Assembly Or _
IsolatedStorageScope.Roaming, Nothing, Nothing)

A domain scope can be added to create a roaming store isolated by user, domain, and application. The following
code example demonstrates this.

IsolatedStorageFile^ isoFile =
IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Assembly | IsolatedStorageScope::Domain |
IsolatedStorageScope::Roaming, (Type^)nullptr, (Type^)nullptr);

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain |
IsolatedStorageScope.Roaming, null, null);

Dim isoFile As IsolatedStorageFile = _


IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Assembly Or IsolatedStorageScope.Domain Or _
IsolatedStorageScope.Roaming, Nothing, Nothing)

See also
IsolatedStorageScope
Isolated Storage
How to: Obtain Stores for Isolated Storage
2 minutes to read • Edit Online

An isolated store exposes a virtual file system within a data compartment. The IsolatedStorageFile class supplies a
number of methods for interacting with an isolated store. To create and retrieve stores, IsolatedStorageFile
provides three static methods:
GetUserStoreForAssembly returns storage that is isolated by user and assembly.
GetUserStoreForDomain returns storage that is isolated by domain and assembly.
Both methods retrieve a store that belongs to the code from which they are called.
The static method GetStore returns an isolated store that is specified by passing in a combination of scope
parameters.
The following code returns a store that is isolated by user, assembly, and domain.

IsolatedStorageFile^ isoStore = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |


IsolatedStorageScope::Assembly | IsolatedStorageScope::Domain, (Type ^)nullptr, (Type ^)nullptr);

IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |


IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null, null);

Dim isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or


IsolatedStorageScope.Assembly Or IsolatedStorageScope.Domain, Nothing, Nothing)

You can use the GetStore method to specify that a store should roam with a roaming user profile. For details on
how to set this up, see Types of Isolation.
Isolated stores obtained from within different assemblies are, by default, different stores. You can access the store
of a different assembly or domain by passing in the assembly or domain evidence in the parameters of the
GetStore method. This requires permission to access isolated storage by application domain identity. For more
information, see the GetStore method overloads.
The GetUserStoreForAssembly, GetUserStoreForDomain, and GetStore methods return an IsolatedStorageFile
object. To help you decide which isolation type is most appropriate for your situation, see Types of Isolation. When
you have an isolated storage file object, you can use the isolated storage methods to read, write, create, and delete
files and directories.
There is no mechanism that prevents code from passing an IsolatedStorageFile object to code that does not have
sufficient access to get the store itself. Domain and assembly identities and isolated storage permissions are
checked only when a reference to an IsolatedStorage object is obtained, typically in the GetUserStoreForAssembly,
GetUserStoreForDomain, or GetStore method. Protecting references to IsolatedStorageFile objects is, therefore,
the responsibility of the code that uses these references.

Example
The following code provides a simple example of a class obtaining a store that is isolated by user and assembly.
The code can be changed to retrieve a store that is isolated by user, domain, and assembly by adding
IsolatedStorageScope.Domain to the arguments that the GetStore method passes.
After you run the code, you can confirm that a store was created by typing StoreAdm /LIST at the command line.
This runs the Isolated Storage tool (Storeadm.exe) and lists all the current isolated stores for the user.

using namespace System;


using namespace System::IO::IsolatedStorage;

public ref class ObtainingAStore


{
public:
static void Main()
{
// Get a new isolated store for this assembly and put it into an
// isolated store object.

IsolatedStorageFile^ isoStore = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |


IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);
}
};

using System;
using System.IO.IsolatedStorage;

public class ObtainingAStore


{
public static void Main()
{
// Get a new isolated store for this assembly and put it into an
// isolated store object.

IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |


IsolatedStorageScope.Assembly, null, null);
}
}

Imports System.IO.IsolatedStorage

Public Class ObtainingAStore


Public Shared Sub Main()
' Get a new isolated store for this assembly and put it into an
' isolated store object.

Dim isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or


IsolatedStorageScope.Assembly, Nothing, Nothing)
End Sub
End Class

See also
IsolatedStorageFile
IsolatedStorageScope
Isolated Storage
Types of Isolation
Assemblies in .NET
How to: Enumerate Stores for Isolated Storage
2 minutes to read • Edit Online

You can enumerate all isolated stores for the current user by using the IsolatedStorageFile.GetEnumerator static
method. This method takes an IsolatedStorageScope value and returns an IsolatedStorageFile enumerator. To
enumerate stores, you must have the IsolatedStorageFilePermission permission that specifies the
AdministerIsolatedStorageByUser value. If you call the GetEnumerator method with the User value, it returns an
array of IsolatedStorageFile objects that are defined for the current user.

Example
The following code example obtains a store that is isolated by user and assembly, creates a few files, and retrieves
those files by using the GetEnumerator method.

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;

public class EnumeratingStores


{
public static void Main()
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null))
{
isoStore.CreateFile("TestFileA.Txt");
isoStore.CreateFile("TestFileB.Txt");
isoStore.CreateFile("TestFileC.Txt");
isoStore.CreateFile("TestFileD.Txt");
}

IEnumerator allFiles = IsolatedStorageFile.GetEnumerator(IsolatedStorageScope.User);


long totalsize = 0;

while (allFiles.MoveNext())
{
IsolatedStorageFile storeFile = (IsolatedStorageFile)allFiles.Current;
totalsize += (long)storeFile.UsedSize;
}

Console.WriteLine("The total size = " + totalsize);


}
}
Imports System.IO
Imports System.IO.IsolatedStorage

Module Module1
Sub Main()
Using isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or
IsolatedStorageScope.Assembly, Nothing, Nothing)
isoStore.CreateFile("TestFileA.Txt")
isoStore.CreateFile("TestFileB.Txt")
isoStore.CreateFile("TestFileC.Txt")
isoStore.CreateFile("TestFileD.Txt")
End Using

Dim allFiles As IEnumerator = IsolatedStorageFile.GetEnumerator(IsolatedStorageScope.User)


Dim totalsize As Long = 0

While (allFiles.MoveNext())
Dim storeFile As IsolatedStorageFile = CType(allFiles.Current, IsolatedStorageFile)
totalsize += CType(storeFile.UsedSize, Long)
End While

Console.WriteLine("The total size = " + totalsize.ToString())

End Sub
End Module

See also
IsolatedStorageFile
Isolated Storage
How to: Delete Stores in Isolated Storage
2 minutes to read • Edit Online

The IsolatedStorageFile class supplies two methods for deleting isolated storage files:
The instance method Remove() does not take any arguments and deletes the store that calls it. No
permissions are required for this operation. Any code that can access the store can delete any or all the data
inside it.
The static method Remove(IsolatedStorageScope) takes the User enumeration value, and deletes all the
stores for the user who is running the code. This operation requires IsolatedStorageFilePermission
permission for the AdministerIsolatedStorageByUser value.

Example
The following code example demonstrates the use of the static and instance Remove methods. The class obtains
two stores; one is isolated for user and assembly and the other is isolated for user, domain, and assembly. The user,
domain, and assembly store is then deleted by calling the Remove() method of the isolated storage file isoStore1 .
Then, all remaining stores for the user are deleted by calling the static method Remove(IsolatedStorageScope).
using namespace System;
using namespace System::IO::IsolatedStorage;

public ref class DeletingStores


{
public:
static void Main()
{
// Get a new isolated store for this user, domain, and assembly.
// Put the store into an IsolatedStorageFile object.

IsolatedStorageFile^ isoStore1 = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |


IsolatedStorageScope::Domain | IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);
Console::WriteLine("A store isolated by user, assembly, and domain has been obtained.");

// Get a new isolated store for user and assembly.


// Put that store into a different IsolatedStorageFile object.

IsolatedStorageFile^ isoStore2 = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |


IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);
Console::WriteLine("A store isolated by user and assembly has been obtained.");

// The Remove method deletes a specific store, in this case the


// isoStore1 file.

isoStore1->Remove();
Console::WriteLine("The user, domain, and assembly isolated store has been deleted.");

// This static method deletes all the isolated stores for this user.

IsolatedStorageFile::Remove(IsolatedStorageScope::User);
Console::WriteLine("All isolated stores for this user have been deleted.");
} // End of Main.
};

int main()
{
DeletingStores::Main();
}
using System;
using System.IO.IsolatedStorage;

public class DeletingStores


{
public static void Main()
{
// Get a new isolated store for this user, domain, and assembly.
// Put the store into an IsolatedStorageFile object.

IsolatedStorageFile isoStore1 = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |


IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null);
Console.WriteLine("A store isolated by user, assembly, and domain has been obtained.");

// Get a new isolated store for user and assembly.


// Put that store into a different IsolatedStorageFile object.

IsolatedStorageFile isoStore2 = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |


IsolatedStorageScope.Assembly, null, null);
Console.WriteLine("A store isolated by user and assembly has been obtained.");

// The Remove method deletes a specific store, in this case the


// isoStore1 file.

isoStore1.Remove();
Console.WriteLine("The user, domain, and assembly isolated store has been deleted.");

// This static method deletes all the isolated stores for this user.

IsolatedStorageFile.Remove(IsolatedStorageScope.User);
Console.WriteLine("All isolated stores for this user have been deleted.");

} // End of Main.
}
Imports System.IO.IsolatedStorage

Public Class DeletingStores


Public Shared Sub Main()
' Get a new isolated store for this user, domain, and assembly.
' Put the store into an IsolatedStorageFile object.

Dim isoStore1 As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or


IsolatedStorageScope.Domain Or IsolatedStorageScope.Assembly, Nothing, Nothing)
Console.WriteLine("A store isolated by user, assembly, and domain has been obtained.")

' Get a new isolated store for user and assembly.


' Put that store into a different IsolatedStorageFile object.

Dim isoStore2 As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or


IsolatedStorageScope.Assembly, Nothing, Nothing)
Console.WriteLine("A store isolated by user and assembly has been obtained.")

' The Remove method deletes a specific store, in this case the
' isoStore1 file.

isoStore1.Remove()
Console.WriteLine("The user, domain, and assembly isolated store has been deleted.")

' This static method deletes all the isolated stores for this user.

IsolatedStorageFile.Remove(IsolatedStorageScope.User)
Console.WriteLine("All isolated stores for this user have been deleted.")

End Sub
End Class

See also
IsolatedStorageFile
Isolated Storage
How to: Anticipate Out-of-Space Conditions with
Isolated Storage
2 minutes to read • Edit Online

Code that uses isolated storage is constrained by a quota that specifies the maximum size for the data
compartment in which isolated storage files and directories exist. The quota is defined by security policy and is
configurable by administrators. If the maximum allowed size is exceeded when you try to write data, an
IsolatedStorageException exception is thrown and the operation fails. This helps prevent malicious denial-of-
service attacks that could cause the application to refuse requests because data storage is filled.
To help you determine whether a given write attempt is likely to fail for this reason, the IsolatedStorage class
provides three read-only properties: AvailableFreeSpace, UsedSize, and Quota. You can use these properties to
determine whether writing to the store will cause the maximum allowed size of the store to be exceeded. Keep in
mind that isolated storage can be accessed concurrently; therefore, when you compute the amount of remaining
storage, the storage space could be consumed by the time you try to write to the store. However, you can use the
maximum size of the store to help determine whether the upper limit on available storage is about to be reached.
The Quota property depends on evidence from the assembly to work properly. For this reason, you should retrieve
this property only on IsolatedStorageFile objects that were created by using the GetUserStoreForAssembly,
GetUserStoreForDomain, or GetStore method. IsolatedStorageFile objects that were created in any other way (for
example, objects that were returned from the GetEnumerator method) will not return an accurate maximum size.

Example
The following code example obtains an isolated store, creates a few files, and retrieves the AvailableFreeSpace
property. The remaining space is reported in bytes.
using namespace System;
using namespace System::IO;
using namespace System::IO::IsolatedStorage;

public ref class CheckingSpace


{
public:
static void Main()
{
// Get an isolated store for this assembly and put it into an
// IsolatedStoreFile object.
IsolatedStorageFile^ isoStore = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);

// Create a few placeholder files in the isolated store.


gcnew IsolatedStorageFileStream("InTheRoot.txt", FileMode::Create, isoStore);
gcnew IsolatedStorageFileStream("Another.txt", FileMode::Create, isoStore);
gcnew IsolatedStorageFileStream("AThird.txt", FileMode::Create, isoStore);
gcnew IsolatedStorageFileStream("AFourth.txt", FileMode::Create, isoStore);
gcnew IsolatedStorageFileStream("AFifth.txt", FileMode::Create, isoStore);

Console::WriteLine(isoStore->AvailableFreeSpace + " bytes of space remain in this isolated store.");


} // End of Main.
};

int main()
{
CheckingSpace::Main();
}

using System;
using System.IO;
using System.IO.IsolatedStorage;

public class CheckingSpace


{
public static void Main()
{
// Get an isolated store for this assembly and put it into an
// IsolatedStoreFile object.
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

// Create a few placeholder files in the isolated store.


new IsolatedStorageFileStream("InTheRoot.txt", FileMode.Create, isoStore);
new IsolatedStorageFileStream("Another.txt", FileMode.Create, isoStore);
new IsolatedStorageFileStream("AThird.txt", FileMode.Create, isoStore);
new IsolatedStorageFileStream("AFourth.txt", FileMode.Create, isoStore);
new IsolatedStorageFileStream("AFifth.txt", FileMode.Create, isoStore);

Console.WriteLine(isoStore.AvailableFreeSpace + " bytes of space remain in this isolated store.");


} // End of Main.
}
Imports System.IO
Imports System.IO.IsolatedStorage

Public Class CheckingSpace


Public Shared Sub Main()
' Get an isolated store for this assembly and put it into an
' IsolatedStoreFile object.
Dim isoStore As IsolatedStorageFile = _
IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Assembly, Nothing, Nothing)

' Create a few placeholder files in the isolated store.


Dim aStream As New IsolatedStorageFileStream("InTheRoot.txt", FileMode.Create, isoStore)
Dim bStream As New IsolatedStorageFileStream("Another.txt", FileMode.Create, isoStore)
Dim cStream As New IsolatedStorageFileStream("AThird.txt", FileMode.Create, isoStore)
Dim dStream As New IsolatedStorageFileStream("AFourth.txt", FileMode.Create, isoStore)
Dim eStream As New IsolatedStorageFileStream("AFifth.txt", FileMode.Create, isoStore)

Console.WriteLine(isoStore.AvailableFreeSpace + " bytes of space remain in this isolated store.")


End Sub
End Class

See also
IsolatedStorageFile
Isolated Storage
How to: Obtain Stores for Isolated Storage
How to: Create Files and Directories in Isolated
Storage
2 minutes to read • Edit Online

After you have obtained an isolated store, you can create directories and files for storing data. Within a store, file
and directory names are specified with respect to the root of the virtual file system.
To create a directory, use the IsolatedStorageFile.CreateDirectory instance method. If you specify a subdirectory of
an directory that doesn't exist, both directories are created. If you specify a directory that already exists, the method
returns without creating a directory, and no exception is thrown. However, if you specify a directory name that
contains invalid characters, an IsolatedStorageException exception is thrown.
To create a file, use the IsolatedStorageFile.CreateFile method.
In the Windows operating system, isolated storage file and directory names are case-insensitive. That is, if you
create a file named ThisFile.txt , and then create another file named THISFILE.TXT , only one file is created. The
file name keeps its original casing for display purposes.

Example
The following code example illustrates how to create files and directories in an isolated store.

using System;
using System.IO;
using System.IO.IsolatedStorage;

public class CreatingFilesDirectories


{
public static void Main()
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null))
{
isoStore.CreateDirectory("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");
isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine("Created directories.");

isoStore.CreateFile("InTheRoot.txt");
Console.WriteLine("Created a new file in the root.");

isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine("Created a new file in the InsideDirectory.");
}
}
}
Imports System.IO
Imports System.IO.IsolatedStorage

Module Module1
Sub Main()
Using isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or
IsolatedStorageScope.Assembly Or IsolatedStorageScope.Domain, Nothing, Nothing)

isoStore.CreateDirectory("TopLevelDirectory")
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel")
isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory")
Console.WriteLine("Created directories.")

isoStore.CreateFile("InTheRoot.txt")
Console.WriteLine("Created a new file in the root.")

isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt")
Console.WriteLine("Created a new file in the InsideDirectory.")
End Using
End Sub
End Module

See also
IsolatedStorageFile
IsolatedStorageFileStream
Isolated Storage
How to: Find Existing Files and Directories in Isolated
Storage
6 minutes to read • Edit Online

To search for a directory in isolated storage, use the IsolatedStorageFile.GetDirectoryNames method. This method
takes a string that represents a search pattern. You can use both single-character (?) and multi-character (*)
wildcard characters in the search pattern, but the wildcard characters must appear in the final portion of the name.
For example, directory1/*ect* is a valid search string, but *ect*/directory2 is not.
To search for a file, use the IsolatedStorageFile.GetFileNames method. The restriction for wildcard characters in
search strings that applies to GetDirectoryNames also applies to GetFileNames.
Neither of these methods is recursive; the IsolatedStorageFile class does not supply any methods for listing all
directories or files in your store. However, recursive methods are shown in the following code example.

Example
The following code example illustrates how to create files and directories in an isolated store. First, a store that is
isolated for user, domain, and assembly is retrieved and placed in the isoStore variable. The CreateDirectory
method is used to set up a few different directories, and the IsolatedStorageFileStream(String, FileMode,
IsolatedStorageFile) constructor creates some files in these directories. The code then loops through the results of
the GetAllDirectories method. This method uses GetDirectoryNames to find all the directory names in the
current directory. These names are stored in an array, and then GetAllDirectories calls itself, passing in each
directory it has found. As a result, all the directory names are returned in an array. Next, the code calls the
GetAllFiles method. This method calls GetAllDirectories to find out the names of all the directories, and then it
checks each directory for files by using the GetFileNames method. The result is returned in an array for display.

using namespace System;


using namespace System::IO;
using namespace System::IO::IsolatedStorage;
using namespace System::Collections;
using namespace System::Collections::Generic;

public class FindingExistingFilesAndDirectories


{
public:
// Retrieves an array of all directories in the store, and
// displays the results.
static void Main()
{
// This part of the code sets up a few directories and files in the
// store.
IsolatedStorageFile^ isoStore = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |
IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);
isoStore->CreateDirectory("TopLevelDirectory");
isoStore->CreateDirectory("TopLevelDirectory/SecondLevel");
isoStore->CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
gcnew IsolatedStorageFileStream("InTheRoot.txt", FileMode::Create, isoStore);
gcnew IsolatedStorageFileStream("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt",
FileMode::Create, isoStore);
// End of setup.

Console::WriteLine('\r');
Console::WriteLine("Here is a list of all directories in this isolated store:");

for each (String^ directory in GetAllDirectories("*", isoStore))


for each (String^ directory in GetAllDirectories("*", isoStore))
{
Console::WriteLine(directory);
}
Console::WriteLine('\r');

// Retrieve all the files in the directory by calling the GetFiles


// method.

Console::WriteLine("Here is a list of all the files in this isolated store:");


for each (String^ file in GetAllFiles("*", isoStore))
{
Console::WriteLine(file);
}

} // End of Main.

// Method to retrieve all directories, recursively, within a store.


static List<String^>^ GetAllDirectories(String^ pattern, IsolatedStorageFile^ storeFile)
{
// Get the root of the search string.
String^ root = Path::GetDirectoryName(pattern);

if (root != "")
{
root += "/";
}

// Retrieve directories.
array<String^>^ directories = storeFile->GetDirectoryNames(pattern);

List<String^>^ directoryList = gcnew List<String^>(directories);

// Retrieve subdirectories of matches.


for (int i = 0, max = directories->Length; i < max; i++)
{
String^ directory = directoryList[i] + "/";
List<String^>^ more = GetAllDirectories (root + directory + "*", storeFile);

// For each subdirectory found, add in the base path.


for (int j = 0; j < more->Count; j++)
{
more[j] = directory + more[j];
}

// Insert the subdirectories into the list and


// update the counter and upper bound.
directoryList->InsertRange(i + 1, more);
i += more->Count;
max += more->Count;
}

return directoryList;
}

static List<String^>^ GetAllFiles(String^ pattern, IsolatedStorageFile^ storeFile)


{
// Get the root and file portions of the search string.
String^ fileString = Path::GetFileName(pattern);
array<String^>^ files = storeFile->GetFileNames(pattern);

List<String^>^ fileList = gcnew List<String^>(files);

// Loop through the subdirectories, collect matches,


// and make separators consistent.
for each (String^ directory in GetAllDirectories( "*", storeFile))
{
for each (String^ file in storeFile->GetFileNames(directory + "/" + fileString))
{
fileList->Add((directory + "/" + file));
fileList->Add((directory + "/" + file));
}
}

return fileList;
} // End of GetFiles.
};

int main()
{
FindingExistingFilesAndDirectories::Main();
}

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Collections.Generic;

public class FindingExistingFilesAndDirectories


{
// Retrieves an array of all directories in the store, and
// displays the results.
public static void Main()
{
// This part of the code sets up a few directories and files in the
// store.
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);
isoStore.CreateDirectory("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");
isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
isoStore.CreateFile("InTheRoot.txt");
isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
// End of setup.

Console.WriteLine('\r');
Console.WriteLine("Here is a list of all directories in this isolated store:");

foreach (string directory in GetAllDirectories("*", isoStore))


{
Console.WriteLine(directory);
}
Console.WriteLine('\r');

// Retrieve all the files in the directory by calling the GetFiles


// method.

Console.WriteLine("Here is a list of all the files in this isolated store:");


foreach (string file in GetAllFiles("*", isoStore)){
Console.WriteLine(file);
}

} // End of Main.

// Method to retrieve all directories, recursively, within a store.


public static List<String> GetAllDirectories(string pattern, IsolatedStorageFile storeFile)
{
// Get the root of the search string.
string root = Path.GetDirectoryName(pattern);

if (root != "")
{
root += "/";
}

// Retrieve directories.
List<String> directoryList = new List<String>(storeFile.GetDirectoryNames(pattern));
List<String> directoryList = new List<String>(storeFile.GetDirectoryNames(pattern));

// Retrieve subdirectories of matches.


for (int i = 0, max = directoryList.Count; i < max; i++)
{
string directory = directoryList[i] + "/";
List<String> more = GetAllDirectories(root + directory + "*", storeFile);

// For each subdirectory found, add in the base path.


for (int j = 0; j < more.Count; j++)
{
more[j] = directory + more[j];
}

// Insert the subdirectories into the list and


// update the counter and upper bound.
directoryList.InsertRange(i + 1, more);
i += more.Count;
max += more.Count;
}

return directoryList;
}

public static List<String> GetAllFiles(string pattern, IsolatedStorageFile storeFile)


{
// Get the root and file portions of the search string.
string fileString = Path.GetFileName(pattern);

List<String> fileList = new List<String>(storeFile.GetFileNames(pattern));

// Loop through the subdirectories, collect matches,


// and make separators consistent.
foreach (string directory in GetAllDirectories("*", storeFile))
{
foreach (string file in storeFile.GetFileNames(directory + "/" + fileString))
{
fileList.Add((directory + "/" + file));
}
}

return fileList;
} // End of GetFiles.
}
Imports System.IO
Imports System.IO.IsolatedStorage
Imports System.Collections
Imports System.Collections.Generic

Public class FindingExistingFilesAndDirectories


' These arrayLists hold the directory and file names as they are found.

Private Shared directoryList As New List(Of String)


Private Shared fileList As New List(Of String)

' Retrieves an array of all directories in the store, and


' displays the results.

Public Shared Sub Main()


' This part of the code sets up a few directories and files in the store.
Dim isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or _
IsolatedStorageScope.Assembly Or IsolatedStorageScope.Domain, Nothing, Nothing)
isoStore.CreateDirectory("TopLevelDirectory")
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel")
isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory")
isoStore.CreateFile("InTheRoot.txt")
isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt")
' End of setup.

Console.WriteLine()
Console.WriteLine("Here is a list of all directories in this isolated store:")

GetAllDirectories("*", isoStore)
For Each directory As String In directoryList
Console.WriteLine(directory)
Next

Console.WriteLine()
Console.WriteLine("Retrieve all the files in the directory by calling the GetFiles method.")

GetAllFiles(isoStore)
For Each file As String In fileList
Console.WriteLine(file)
Next
End Sub

Public Shared Sub GetAllDirectories(ByVal pattern As String, ByVal storeFile As IsolatedStorageFile)


' Retrieve directories.
Dim directories As String() = storeFile.GetDirectoryNames(pattern)

For Each directory As String In directories


' Add the directory to the final list.
directoryList.Add((pattern.TrimEnd(CChar("*"))) + directory + "/")
' Call the method again using directory.
GetAllDirectories((pattern.TrimEnd(CChar("*")) + directory + "/*"), storeFile)
Next
End Sub

Public Shared Sub GetAllFiles(ByVal storefile As IsolatedStorageFile)


' This adds the root to the directory list.
directoryList.Add("*")
For Each directory As String In directoryList
Dim files As String() = storefile.GetFileNames(directory + "*")
For Each dirfile As String In files
fileList.Add(dirfile)
Next
Next
End Sub
End Class
See also
IsolatedStorageFile
Isolated Storage
How to: Read and Write to Files in Isolated Storage
2 minutes to read • Edit Online

To read from, or write to, a file in an isolated store, use an IsolatedStorageFileStream object with a stream reader
(StreamReader object) or stream writer (StreamWriter object).

Example
The following code example obtains an isolated store and checks whether a file named TestStore.txt exists in the
store. If it doesn't exist, it creates the file and writes "Hello Isolated Storage" to the file. If TestStore.txt already exists,
the example code reads from the file.

using System;
using System.IO;
using System.IO.IsolatedStorage;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

if (isoStore.FileExists("TestStore.txt"))
{
Console.WriteLine("The file already exists!");
using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("TestStore.txt",
FileMode.Open, isoStore))
{
using (StreamReader reader = new StreamReader(isoStream))
{
Console.WriteLine("Reading contents:");
Console.WriteLine(reader.ReadToEnd());
}
}
}
else
{
using (IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("TestStore.txt",
FileMode.CreateNew, isoStore))
{
using (StreamWriter writer = new StreamWriter(isoStream))
{
writer.WriteLine("Hello Isolated Storage");
Console.WriteLine("You have written to the file.");
}
}
}
}
}
}
Imports System.IO
Imports System.IO.IsolatedStorage

Module Module1

Sub Main()
Dim isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or
IsolatedStorageScope.Assembly, Nothing, Nothing)

If (isoStore.FileExists("TestStore.txt")) Then
Console.WriteLine("The file already exists!")
Using isoStream As IsolatedStorageFileStream = New IsolatedStorageFileStream("TestStore.txt",
FileMode.Open, isoStore)
Using reader As StreamReader = New StreamReader(isoStream)
Console.WriteLine("Reading contents:")
Console.WriteLine(reader.ReadToEnd())
End Using
End Using
Else
Using isoStream As IsolatedStorageFileStream = New IsolatedStorageFileStream("TestStore.txt",
FileMode.CreateNew, isoStore)
Using writer As StreamWriter = New StreamWriter(isoStream)
writer.WriteLine("Hello Isolated Storage")
Console.WriteLine("You have written to the file.")
End Using
End Using
End If
End Sub

End Module

See also
IsolatedStorageFile
IsolatedStorageFileStream
System.IO.FileMode
System.IO.FileAccess
System.IO.StreamReader
System.IO.StreamWriter
File and Stream I/O
Isolated Storage
How to: Delete Files and Directories in Isolated
Storage
3 minutes to read • Edit Online

You can delete directories and files within an isolated storage file. Within a store, file and directory names are
operating-system dependent and are specified as relative to the root of the virtual file system. They are not case-
sensitive on Windows operating systems.
The System.IO.IsolatedStorage.IsolatedStorageFile class supplies two methods for deleting directories and files:
DeleteDirectory and DeleteFile. An IsolatedStorageException exception is thrown if you try to delete a file or
directory that does not exist. If you include a wildcard character in the name, DeleteDirectory throws an
IsolatedStorageException exception, and DeleteFile throws an ArgumentException exception.
The DeleteDirectory method fails if the directory contains any files or subdirectories. You can use the
GetFileNames and GetDirectoryNames methods to retrieve the existing files and directories. For more information
about searching the virtual file system of a store, see How to: Find Existing Files and Directories in Isolated
Storage.

Example
The following code example creates and then deletes several directories and files.

using namespace System;


using namespace System::IO::IsolatedStorage;
using namespace System::IO;

public ref class DeletingFilesDirectories


{
public:
static void Main()
{
// Get a new isolated store for this user domain and assembly.
// Put the store into an isolatedStorageFile object.

IsolatedStorageFile^ isoStore = IsolatedStorageFile::GetStore(IsolatedStorageScope::User |


IsolatedStorageScope::Domain | IsolatedStorageScope::Assembly, (Type ^)nullptr, (Type ^)nullptr);

Console::WriteLine("Creating Directories:");

// This code creates several different directories.

isoStore->CreateDirectory("TopLevelDirectory");
Console::WriteLine("TopLevelDirectory");
isoStore->CreateDirectory("TopLevelDirectory/SecondLevel");
Console::WriteLine("TopLevelDirectory/SecondLevel");

// This code creates two new directories, one inside the other.

isoStore->CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
Console::WriteLine("AnotherTopLevelDirectory/InsideDirectory");
Console::WriteLine();

// This code creates a few files and places them in the directories.

Console::WriteLine("Creating Files:");

// This file is placed in the root.


IsolatedStorageFileStream^ isoStream1 = gcnew IsolatedStorageFileStream("InTheRoot.txt",
FileMode::Create, isoStore);
Console::WriteLine("InTheRoot.txt");

isoStream1->Close();

// This file is placed in the InsideDirectory.

IsolatedStorageFileStream^ isoStream2 = gcnew IsolatedStorageFileStream(


"AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt", FileMode::Create, isoStore);
Console::WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console::WriteLine();

isoStream2->Close();

Console::WriteLine("Deleting File:");

// This code deletes the HereIAm.txt file.


isoStore->DeleteFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console::WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console::WriteLine();

Console::WriteLine("Deleting Directory:");

// This code deletes the InsideDirectory.

isoStore->DeleteDirectory("AnotherTopLevelDirectory/InsideDirectory/");
Console::WriteLine("AnotherTopLevelDirectory/InsideDirectory/");
Console::WriteLine();

} // End of main.
};

int main()
{
DeletingFilesDirectories::Main();
}

using System;
using System.IO.IsolatedStorage;
using System.IO;

public class DeletingFilesDirectories


{
public static void Main()
{
// Get a new isolated store for this user domain and assembly.
// Put the store into an isolatedStorageFile object.

IsolatedStorageFile isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |


IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null);

Console.WriteLine("Creating Directories:");

// This code creates several different directories.

isoStore.CreateDirectory("TopLevelDirectory");
Console.WriteLine("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");
Console.WriteLine("TopLevelDirectory/SecondLevel");

// This code creates two new directories, one inside the other.

isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine();
// This code creates a few files and places them in the directories.

Console.WriteLine("Creating Files:");

// This file is placed in the root.

IsolatedStorageFileStream isoStream1 = new IsolatedStorageFileStream("InTheRoot.txt",


FileMode.Create, isoStore);
Console.WriteLine("InTheRoot.txt");

isoStream1.Close();

// This file is placed in the InsideDirectory.

IsolatedStorageFileStream isoStream2 = new IsolatedStorageFileStream(


"AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt", FileMode.Create, isoStore);
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine();

isoStream2.Close();

Console.WriteLine("Deleting File:");

// This code deletes the HereIAm.txt file.


isoStore.DeleteFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine();

Console.WriteLine("Deleting Directory:");

// This code deletes the InsideDirectory.

isoStore.DeleteDirectory("AnotherTopLevelDirectory/InsideDirectory/");
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/");
Console.WriteLine();

} // End of main.
}
Imports System.IO.IsolatedStorage
Imports System.IO

Public Class DeletingFilesDirectories


Public Shared Sub Main()
' Get a new isolated store for this user domain and assembly.
' Put the store into an isolatedStorageFile object.

Dim isoStore As IsolatedStorageFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User Or


IsolatedStorageScope.Domain Or IsolatedStorageScope.Assembly, Nothing, Nothing)

Console.WriteLine("Creating Directories:")

' This code creates several different directories.

isoStore.CreateDirectory("TopLevelDirectory")
Console.WriteLine("TopLevelDirectory")
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel")
Console.WriteLine("TopLevelDirectory/SecondLevel")

' This code creates two new directories, one inside the other.

isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory")
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory")
Console.WriteLine()

' This code creates a few files and places them in the directories.

Console.WriteLine("Creating Files:")

' This file is placed in the root.

Dim isoStream1 As New IsolatedStorageFileStream("InTheRoot.txt", FileMode.Create, isoStore)


Console.WriteLine("InTheRoot.txt")

isoStream1.Close()

' This file is placed in the InsideDirectory.

Dim isoStream2 As New IsolatedStorageFileStream(


"AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt", FileMode.Create, isoStore)
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt")
Console.WriteLine()

isoStream2.Close()

Console.WriteLine("Deleting File:")

' This code deletes the HereIAm.txt file.


isoStore.DeleteFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt")
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt")
Console.WriteLine()

Console.WriteLine("Deleting Directory:")

' This code deletes the InsideDirectory.

isoStore.DeleteDirectory("AnotherTopLevelDirectory/InsideDirectory/")
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/")
Console.WriteLine()

End Sub
End Class

See also
System.IO.IsolatedStorage.IsolatedStorageFile
Isolated Storage
Pipe Operations in .NET
2 minutes to read • Edit Online

Pipes provide a means for interprocess communication. There are two types of pipes:
Anonymous pipes.
Anonymous pipes provide interprocess communication on a local computer. Anonymous pipes require less
overhead than named pipes but offer limited services. Anonymous pipes are one-way and cannot be used
over a network. They support only a single server instance. Anonymous pipes are useful for communication
between threads, or between parent and child processes where the pipe handles can be easily passed to the
child process when it is created.
In .NET, you implement anonymous pipes by using the AnonymousPipeServerStream and
AnonymousPipeClientStream classes.
See How to: Use Anonymous Pipes for Local Interprocess Communication.
Named pipes.
Named pipes provide interprocess communication between a pipe server and one or more pipe clients.
Named pipes can be one-way or duplex. They support message-based communication and allow multiple
clients to connect simultaneously to the server process using the same pipe name. Named pipes also
support impersonation, which enables connecting processes to use their own permissions on remote
servers.
In .NET, you implement named pipes by using the NamedPipeServerStream and NamedPipeClientStream
classes.
See How to: Use Named Pipes for Network Interprocess Communication.

See also
File and Stream I/O
How to: Use Anonymous Pipes for Local Interprocess Communication
How to: Use Named Pipes for Network Interprocess Communication
How to: Use Anonymous Pipes for Local Interprocess
Communication
4 minutes to read • Edit Online

Anonymous pipes provide interprocess communication on a local computer. They offer less functionality than
named pipes, but also require less overhead. You can use anonymous pipes to make interprocess communication
on a local computer easier. You cannot use anonymous pipes for communication over a network.
To implement anonymous pipes, use the AnonymousPipeServerStream and AnonymousPipeClientStream classes.

Example
The following example demonstrates a way to send a string from a parent process to a child process using
anonymous pipes. This example creates an AnonymousPipeServerStream object in a parent process with a
PipeDirection value of Out. The parent process then creates a child process by using a client handle to create an
AnonymousPipeClientStream object. The child process has a PipeDirection value of In.
The parent process then sends a user-supplied string to the child process. The string is displayed to the console in
the child process.
The following example shows the server process.
#using <System.dll>
#using <System.Core.dll>

using namespace System;


using namespace System::IO;
using namespace System::IO::Pipes;
using namespace System::Diagnostics;

ref class PipeServer


{
public:
static void Main()
{
Process^ pipeClient = gcnew Process();

pipeClient->StartInfo->FileName = "pipeClient.exe";

AnonymousPipeServerStream^ pipeServer =
gcnew AnonymousPipeServerStream(PipeDirection::Out,
HandleInheritability::Inheritable);

Console::WriteLine("[SERVER] Current TransmissionMode: {0}.",


pipeServer->TransmissionMode);

// Pass the client process a handle to the server.


pipeClient->StartInfo->Arguments =
pipeServer->GetClientHandleAsString();
pipeClient->StartInfo->UseShellExecute = false;
pipeClient->Start();

pipeServer->DisposeLocalCopyOfClientHandle();

try
{
// Read user input and send that to the client process.
StreamWriter^ sw = gcnew StreamWriter(pipeServer);

sw->AutoFlush = true;
// Send a 'sync message' and wait for client to receive it.
sw->WriteLine("SYNC");
pipeServer->WaitForPipeDrain();
// Send the console input to the client process.
Console::Write("[SERVER] Enter text: ");
sw->WriteLine(Console::ReadLine());
sw->Close();
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException^ e)
{
Console::WriteLine("[SERVER] Error: {0}", e->Message);
}
pipeServer->Close();
pipeClient->WaitForExit();
pipeClient->Close();
Console::WriteLine("[SERVER] Client quit. Server terminating.");
}
};

int main()
{
PipeServer::Main();
}
using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics;

class PipeServer
{
static void Main()
{
Process pipeClient = new Process();

pipeClient.StartInfo.FileName = "pipeClient.exe";

using (AnonymousPipeServerStream pipeServer =


new AnonymousPipeServerStream(PipeDirection.Out,
HandleInheritability.Inheritable))
{
Console.WriteLine("[SERVER] Current TransmissionMode: {0}.",
pipeServer.TransmissionMode);

// Pass the client process a handle to the server.


pipeClient.StartInfo.Arguments =
pipeServer.GetClientHandleAsString();
pipeClient.StartInfo.UseShellExecute = false;
pipeClient.Start();

pipeServer.DisposeLocalCopyOfClientHandle();

try
{
// Read user input and send that to the client process.
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
// Send a 'sync message' and wait for client to receive it.
sw.WriteLine("SYNC");
pipeServer.WaitForPipeDrain();
// Send the console input to the client process.
Console.Write("[SERVER] Enter text: ");
sw.WriteLine(Console.ReadLine());
}
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("[SERVER] Error: {0}", e.Message);
}
}

pipeClient.WaitForExit();
pipeClient.Close();
Console.WriteLine("[SERVER] Client quit. Server terminating.");
}
}
Imports System.IO
Imports System.IO.Pipes
Imports System.Diagnostics

Class PipeServer
Shared Sub Main()
Dim pipeClient As New Process()

pipeClient.StartInfo.FileName = "pipeClient.exe"

Using pipeServer As New AnonymousPipeServerStream(PipeDirection.Out, _


HandleInheritability.Inheritable)

Console.WriteLine("[SERVER] Current TransmissionMode: {0}.",


pipeServer.TransmissionMode)

' Pass the client process a handle to the server.


pipeClient.StartInfo.Arguments = pipeServer.GetClientHandleAsString()
pipeClient.StartInfo.UseShellExecute = false
pipeClient.Start()

pipeServer.DisposeLocalCopyOfClientHandle()

Try
' Read user input and send that to the client process.
Using sw As New StreamWriter(pipeServer)
sw.AutoFlush = true
' Send a 'sync message' and wait for client to receive it.
sw.WriteLine("SYNC")
pipeServer.WaitForPipeDrain()
' Send the console input to the client process.
Console.Write("[SERVER] Enter text: ")
sw.WriteLine(Console.ReadLine())
End Using
Catch e As IOException
' Catch the IOException that is raised if the pipe is broken
' or disconnected.
Console.WriteLine("[SERVER] Error: {0}", e.Message)
End Try
End Using

pipeClient.WaitForExit()
pipeClient.Close()
Console.WriteLine("[SERVER] Client quit. Server terminating.")
End Sub
End Class

Example
The following example shows the client process. The server process starts the client process and gives that process
a client handle. The resulting executable from the client code should be named pipeClient.exe and be copied to
the same directory as the server executable before running the server process.
#using <System.Core.dll>

using namespace System;


using namespace System::IO;
using namespace System::IO::Pipes;

ref class PipeClient


{
public:
static void Main(array<String^>^ args)
{
if (args->Length > 1)
{
PipeStream^ pipeClient = gcnew AnonymousPipeClientStream(PipeDirection::In, args[1]);

Console::WriteLine("[CLIENT] Current TransmissionMode: {0}.",


pipeClient->TransmissionMode);

StreamReader^ sr = gcnew StreamReader(pipeClient);

// Display the read text to the console


String^ temp;

// Wait for 'sync message' from the server.


do
{
Console::WriteLine("[CLIENT] Wait for sync...");
temp = sr->ReadLine();
}
while (!temp->StartsWith("SYNC"));

// Read the server data and echo to the console.


while ((temp = sr->ReadLine()) != nullptr)
{
Console::WriteLine("[CLIENT] Echo: " + temp);
}
sr->Close();
pipeClient->Close();
}
Console::Write("[CLIENT] Press Enter to continue...");
Console::ReadLine();
}
};

int main()
{
array<String^>^ args = Environment::GetCommandLineArgs();
PipeClient::Main(args);
}
using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
static void Main(string[] args)
{
if (args.Length > 0)
{
using (PipeStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In, args[0]))
{
Console.WriteLine("[CLIENT] Current TransmissionMode: {0}.",
pipeClient.TransmissionMode);

using (StreamReader sr = new StreamReader(pipeClient))


{
// Display the read text to the console
string temp;

// Wait for 'sync message' from the server.


do
{
Console.WriteLine("[CLIENT] Wait for sync...");
temp = sr.ReadLine();
}
while (!temp.StartsWith("SYNC"));

// Read the server data and echo to the console.


while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("[CLIENT] Echo: " + temp);
}
}
}
}
Console.Write("[CLIENT] Press Enter to continue...");
Console.ReadLine();
}
}
Imports System.IO
Imports System.IO.Pipes

Class PipeClient
Shared Sub Main(args() as String)
If args.Length > 0 Then
Using pipeClient As New AnonymousPipeClientStream(PipeDirection.In, args(0))
Console.WriteLine("[CLIENT] Current TransmissionMode: {0}.", _
pipeClient.TransmissionMode)

Using sr As New StreamReader(pipeClient)


' Display the read text to the console
Dim temp As String

' Wait for 'sync message' from the server.


Do
Console.WriteLine("[CLIENT] Wait for sync...")
temp = sr.ReadLine()
Loop While temp.StartsWith("SYNC") = False

' Read the server data and echo to the console.


temp = sr.ReadLine()
While Not temp = Nothing
Console.WriteLine("[CLIENT] Echo: " + temp)
temp = sr.ReadLine()
End While
End Using
End Using
End If
Console.Write("[CLIENT] Press Enter to continue...")
Console.ReadLine()
End Sub
End Class

See also
Pipes
How to: Use Named Pipes for Network Interprocess Communication
How to: Use Named Pipes for Network Interprocess
Communication
11 minutes to read • Edit Online

Named pipes provide interprocess communication between a pipe server and one or more pipe clients. They offer
more functionality than anonymous pipes, which provide interprocess communication on a local computer.
Named pipes support full duplex communication over a network and multiple server instances, message-based
communication, and client impersonation, which enables connecting processes to use their own set of permissions
on remote servers.
To implement name pipes, use the NamedPipeServerStream and NamedPipeClientStream classes.

Example
The following example demonstrates how to create a named pipe by using the NamedPipeServerStream class. In
this example, the server process creates four threads. Each thread can accept a client connection. The connected
client process then supplies the server with a file name. If the client has sufficient permissions, the server process
opens the file and sends its contents back to the client.

#using <System.Core.dll>

using namespace System;


using namespace System::IO;
using namespace System::IO::Pipes;
using namespace System::Text;
using namespace System::Threading;

// Defines the data protocol for reading and writing strings on our stream
public ref class StreamString
{
private:
Stream^ ioStream;
UnicodeEncoding^ streamEncoding;

public:
StreamString(Stream^ ioStream)
{
this->ioStream = ioStream;
streamEncoding = gcnew UnicodeEncoding();
}

String^ ReadString()
{
int len;

len = ioStream->ReadByte() * 256;


len += ioStream->ReadByte();
array<Byte>^ inBuffer = gcnew array<Byte>(len);
ioStream->Read(inBuffer, 0, len);

return streamEncoding->GetString(inBuffer);
}

int WriteString(String^ outString)


{
array<Byte>^ outBuffer = streamEncoding->GetBytes(outString);
int len = outBuffer->Length;
if (len > UInt16::MaxValue)
if (len > UInt16::MaxValue)
{
len = (int)UInt16::MaxValue;
}
ioStream->WriteByte((Byte)(len / 256));
ioStream->WriteByte((Byte)(len & 255));
ioStream->Write(outBuffer, 0, len);
ioStream->Flush();

return outBuffer->Length + 2;
}
};

// Contains the method executed in the context of the impersonated user


public ref class ReadFileToStream
{
private:
String^ fn;
StreamString ^ss;

public:
ReadFileToStream(StreamString^ str, String^ filename)
{
fn = filename;
ss = str;
}

void Start()
{
String^ contents = File::ReadAllText(fn);
ss->WriteString(contents);
}
};

public ref class PipeServer


{
private:
static int numThreads = 4;

public:
static void Main()
{
int i;
array<Thread^>^ servers = gcnew array<Thread^>(numThreads);

Console::WriteLine("\n*** Named pipe server stream with impersonation example ***\n");


Console::WriteLine("Waiting for client connect...\n");
for (i = 0; i < numThreads; i++)
{
servers[i] = gcnew Thread(gcnew ThreadStart(&ServerThread));
servers[i]->Start();
}
Thread::Sleep(250);
while (i > 0)
{
for (int j = 0; j < numThreads; j++)
{
if (servers[j] != nullptr)
{
if (servers[j]->Join(250))
{
Console::WriteLine("Server thread[{0}] finished.", servers[j]->ManagedThreadId);
servers[j] = nullptr;
i--; // decrement the thread watch count
}
}
}
}
Console::WriteLine("\nServer threads exhausted, exiting.");
}
}

private:
static void ServerThread()
{
NamedPipeServerStream^ pipeServer =
gcnew NamedPipeServerStream("testpipe", PipeDirection::InOut, numThreads);

int threadId = Thread::CurrentThread->ManagedThreadId;

// Wait for a client to connect


pipeServer->WaitForConnection();

Console::WriteLine("Client connected on thread[{0}].", threadId);


try
{
// Read the request from the client. Once the client has
// written to the pipe its security token will be available.

StreamString^ ss = gcnew StreamString(pipeServer);

// Verify our identity to the connected client using a


// string that the client anticipates.

ss->WriteString("I am the one true server!");


String^ filename = ss->ReadString();

// Read in the contents of the file while impersonating the client.


ReadFileToStream^ fileReader = gcnew ReadFileToStream(ss, filename);

// Display the name of the user we are impersonating.


Console::WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer->GetImpersonationUserName());
pipeServer->RunAsClient(gcnew PipeStreamImpersonationWorker(fileReader,
&ReadFileToStream::Start));
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException^ e)
{
Console::WriteLine("ERROR: {0}", e->Message);
}
pipeServer->Close();
}
};

int main()
{
PipeServer::Main();
}

using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PipeServer


{
private static int numThreads = 4;

public static void Main()


{
int i;
Thread[] servers = new Thread[numThreads];

Console.WriteLine("\n*** Named pipe server stream with impersonation example ***\n");


Console.WriteLine("Waiting for client connect...\n");
Console.WriteLine("Waiting for client connect...\n");
for (i = 0; i < numThreads; i++)
{
servers[i] = new Thread(ServerThread);
servers[i].Start();
}
Thread.Sleep(250);
while (i > 0)
{
for (int j = 0; j < numThreads; j++)
{
if (servers[j] != null)
{
if (servers[j].Join(250))
{
Console.WriteLine("Server thread[{0}] finished.", servers[j].ManagedThreadId);
servers[j] = null;
i--; // decrement the thread watch count
}
}
}
}
Console.WriteLine("\nServer threads exhausted, exiting.");
}

private static void ServerThread(object data)


{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);

int threadId = Thread.CurrentThread.ManagedThreadId;

// Wait for a client to connect


pipeServer.WaitForConnection();

Console.WriteLine("Client connected on thread[{0}].", threadId);


try
{
// Read the request from the client. Once the client has
// written to the pipe its security token will be available.

StreamString ss = new StreamString(pipeServer);

// Verify our identity to the connected client using a


// string that the client anticipates.

ss.WriteString("I am the one true server!");


string filename = ss.ReadString();

// Read in the contents of the file while impersonating the client.


ReadFileToStream fileReader = new ReadFileToStream(ss, filename);

// Display the name of the user we are impersonating.


Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer.GetImpersonationUserName());
pipeServer.RunAsClient(fileReader.Start);
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Close();
}
}

// Defines the data protocol for reading and writing strings on our stream
public class StreamString
{
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;

public StreamString(Stream ioStream)


{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}

public string ReadString()


{
int len = 0;

len = ioStream.ReadByte() * 256;


len += ioStream.ReadByte();
byte[] inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);

return streamEncoding.GetString(inBuffer);
}

public int WriteString(string outString)


{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();

return outBuffer.Length + 2;
}
}

// Contains the method executed in the context of the impersonated user


public class ReadFileToStream
{
private string fn;
private StreamString ss;

public ReadFileToStream(StreamString str, string filename)


{
fn = filename;
ss = str;
}

public void Start()


{
string contents = File.ReadAllText(fn);
ss.WriteString(contents);
}
}

Imports System.IO
Imports System.IO.Pipes
Imports System.Text
Imports System.Threading

Public Class PipeServer


Private Shared numThreads As Integer = 4

Public Shared Sub Main()


Dim i As Integer
Dim i As Integer
Dim servers(numThreads) As Thread

Console.WriteLine(vbNewLine + "*** Named pipe server stream with impersonation example ***" +
vbNewLine)
Console.WriteLine("Waiting for client connect..." + vbNewLine)
For i = 0 To numThreads - 1
servers(i) = New Thread(AddressOf ServerThread)
servers(i).Start()
Next i
Thread.Sleep(250)
While i > 0
For j As Integer = 0 To numThreads - 1
If Not(servers(j) Is Nothing) Then
if servers(j).Join(250)
Console.WriteLine("Server thread[{0}] finished.", servers(j).ManagedThreadId)
servers(j) = Nothing
i -= 1 ' decrement the thread watch count
End If
End If
Next j
End While
Console.WriteLine(vbNewLine + "Server threads exhausted, exiting.")
End Sub

Private Shared Sub ServerThread(data As Object)


Dim pipeServer As New _
NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads)

Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId

' Wait for a client to connect


pipeServer.WaitForConnection()

Console.WriteLine("Client connected on thread[{0}].", threadId)


Try
' Read the request from the client. Once the client has
' written to the pipe its security token will be available.

Dim ss As new StreamString(pipeServer)

' Verify our identity to the connected client using a


' string that the client anticipates.

ss.WriteString("I am the one true server!")


Dim filename As String = ss.ReadString()

' Read in the contents of the file while impersonating the client.
Dim fileReader As New ReadFileToStream(ss, filename)

' Display the name of the user we are impersonating.


Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer.GetImpersonationUserName())
pipeServer.RunAsClient(AddressOf fileReader.Start)
' Catch the IOException that is raised if the pipe is broken
' or disconnected.
Catch e As IOException
Console.WriteLine("ERROR: {0}", e.Message)
End Try
pipeServer.Close()
End Sub
End Class

' Defines the data protocol for reading and writing strings on our stream
Public Class StreamString
Private ioStream As Stream
Private streamEncoding As UnicodeEncoding

Public Sub New(ioStream As Stream)


Me.ioStream = ioStream
Me.ioStream = ioStream
streamEncoding = New UnicodeEncoding(False, False)
End Sub

Public Function ReadString() As String


Dim len As Integer = 0
len = CType(ioStream.ReadByte(), Integer) * 256
len += CType(ioStream.ReadByte(), Integer)
Dim inBuffer As Array = Array.CreateInstance(GetType(Byte), len)
ioStream.Read(inBuffer, 0, len)

Return streamEncoding.GetString(inBuffer)
End Function

Public Function WriteString(outString As String) As Integer


Dim outBuffer() As Byte = streamEncoding.GetBytes(outString)
Dim len As Integer = outBuffer.Length
If len > UInt16.MaxValue Then
len = CType(UInt16.MaxValue, Integer)
End If
ioStream.WriteByte(CType(len \ 256, Byte))
ioStream.WriteByte(CType(len And 255, Byte))
ioStream.Write(outBuffer, 0, outBuffer.Length)
ioStream.Flush()

Return outBuffer.Length + 2
End Function
End Class

' Contains the method executed in the context of the impersonated user
Public Class ReadFileToStream
Private fn As String
Private ss As StreamString

Public Sub New(str As StreamString, filename As String)


fn = filename
ss = str
End Sub

Public Sub Start()


Dim contents As String = File.ReadAllText(fn)
ss.WriteString(contents)
End Sub
End Class

Example
The following example shows the client process, which uses the NamedPipeClientStream class. The client
connects to the server process and sends a file name to the server. The example uses impersonation, so the
identity that is running the client application must have permission to access the file. The server then sends the
contents of the file back to the client. The file contents are then displayed to the console.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Security.Principal;
using System.Text;
using System.Threading;

public class PipeClient


{
private static int numClients = 4;

public static void Main(string[] args)


{
{
if (args.Length > 0)
{
if (args[0] == "spawnclient")
{
var pipeClient =
new NamedPipeClientStream(".", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);

Console.WriteLine("Connecting to server...\n");
pipeClient.Connect();

var ss = new StreamString(pipeClient);


// Validate the server's signature string.
if (ss.ReadString() == "I am the one true server!")
{
// The client security token is sent with the first write.
// Send the name of the file whose contents are returned
// by the server.
ss.WriteString("c:\\textfile.txt");

// Print the file to the screen.


Console.Write(ss.ReadString());
}
else
{
Console.WriteLine("Server could not be verified.");
}
pipeClient.Close();
// Give the client process some time to display results before exiting.
Thread.Sleep(4000);
}
}
else
{
Console.WriteLine("\n*** Named pipe client stream with impersonation example ***\n");
StartClients();
}
}

// Helper function to create pipe client processes


private static void StartClients()
{
string currentProcessName = Environment.CommandLine;
currentProcessName = Path.ChangeExtension(currentProcessName, ".exe");
Process[] plist = new Process[numClients];

Console.WriteLine("Spawning client processes...\n");

if (currentProcessName.Contains(Environment.CurrentDirectory))
{
currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
}

// Remove extra characters when launched from Visual Studio


currentProcessName = currentProcessName.Replace("\\", String.Empty);
currentProcessName = currentProcessName.Replace("\"", String.Empty);

int i;
for (i = 0; i < numClients; i++)
{
// Start 'this' program but spawn a named pipe client.
plist[i] = Process.Start(currentProcessName, "spawnclient");
}
while (i > 0)
{
for (int j = 0; j < numClients; j++)
{
if (plist[j] != null)
if (plist[j] != null)
{
if (plist[j].HasExited)
{
Console.WriteLine($"Client process[{plist[j].Id}] has exited.");
plist[j] = null;
i--; // decrement the process watch count
}
else
{
Thread.Sleep(250);
}
}
}
}
Console.WriteLine("\nClient processes finished, exiting.");
}
}

// Defines the data protocol for reading and writing strings on our stream.
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;

public StreamString(Stream ioStream)


{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}

public string ReadString()


{
int len;
len = ioStream.ReadByte() * 256;
len += ioStream.ReadByte();
var inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);

return streamEncoding.GetString(inBuffer);
}

public int WriteString(string outString)


{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();

return outBuffer.Length + 2;
}
}

Imports System.Diagnostics
Imports System.IO
Imports System.IO.Pipes
Imports System.Security.Principal
Imports System.Text
Imports System.Threading

Public Class PipeClient


Private Shared numClients As Integer = 4
Private Shared numClients As Integer = 4

Public Shared Sub Main(args() As String)


If args.Length > 0 Then
If args(0) = "spawnclient" Then
Dim pipeClient As New NamedPipeClientStream( _
".", "testpipe", _
PipeDirection.InOut, PipeOptions.None, _
TokenImpersonationLevel.Impersonation)

Console.WriteLine("Connecting to server..." + vbNewLine)


pipeClient.Connect()

Dim ss As New StreamString(pipeClient)


' Validate the server's signature string.
If ss.ReadString() = "I am the one true server!" Then
' The client security token is sent with the first write.
' Send the name of the file whose contents are returned
' by the server.
ss.WriteString("c:\textfile.txt")

' Print the file to the screen.


Console.Write(ss.ReadString())
Else
Console.WriteLine("Server could not be verified.")
End If
pipeClient.Close()
' Give the client process some time to display results before exiting.
Thread.Sleep(4000)
End If
Else
Console.WriteLine(vbNewLine + "*** Named pipe client stream with impersonation example ***" +
vbNewLine)
StartClients()
End If
End Sub

' Helper function to create pipe client processes


Private Shared Sub StartClients()
Dim currentProcessName As String = Environment.CommandLine
Dim plist(numClients - 1) As Process

Console.WriteLine("Spawning client processes..." + vbNewLine)

If currentProcessName.Contains(Environment.CurrentDirectory) Then
currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty)
End If

' Remove extra characters when launched from Visual Studio.


currentProcessName = currentProcessName.Replace("\", String.Empty)
currentProcessName = currentProcessName.Replace("""", String.Empty)

' Change extension for .NET Core "dotnet run" returns the DLL, not the host exe.
currentProcessName = Path.ChangeExtension(currentProcessName, ".exe")

Dim i As Integer
For i = 0 To numClients - 1
' Start 'this' program but spawn a named pipe client.
plist(i) = Process.Start(currentProcessName, "spawnclient")
Next
While i > 0
For j As Integer = 0 To numClients - 1
If plist(j) IsNot Nothing Then
If plist(j).HasExited Then
Console.WriteLine($"Client process[{plist(j).Id}] has exited.")
plist(j) = Nothing
i -= 1 ' decrement the process watch count
Else
Thread.Sleep(250)
End If
End If
Next
End While
Console.WriteLine(vbNewLine + "Client processes finished, exiting.")
End Sub
End Class

' Defines the data protocol for reading and writing strings on our stream
Public Class StreamString
Private ioStream As Stream
Private streamEncoding As UnicodeEncoding

Public Sub New(ioStream As Stream)


Me.ioStream = ioStream
streamEncoding = New UnicodeEncoding(False, False)
End Sub

Public Function ReadString() As String


Dim len As Integer = 0
len = CType(ioStream.ReadByte(), Integer) * 256
len += CType(ioStream.ReadByte(), Integer)
Dim inBuffer As Array = Array.CreateInstance(GetType(Byte), len)
ioStream.Read(inBuffer, 0, len)

Return streamEncoding.GetString(inBuffer)
End Function

Public Function WriteString(outString As String) As Integer


Dim outBuffer As Byte() = streamEncoding.GetBytes(outString)
Dim len As Integer = outBuffer.Length
If len > UInt16.MaxValue Then
len = CType(UInt16.MaxValue, Integer)
End If
ioStream.WriteByte(CType(len \ 256, Byte))
ioStream.WriteByte(CType(len And 255, Byte))
ioStream.Write(outBuffer, 0, outBuffer.Length)
ioStream.Flush()

Return outBuffer.Length + 2
End Function
End Class

Robust Programming
The client and server processes in this example are intended to run on the same computer, so the server name
provided to the NamedPipeClientStream object is "." . If the client and server processes were on separate
computers, "." would be replaced with the network name of the computer that runs the server process.

See also
TokenImpersonationLevel
GetImpersonationUserName
Pipes
How to: Use Anonymous Pipes for Local Interprocess Communication
System.IO.Pipelines in .NET
19 minutes to read • Edit Online

System.IO.Pipelines is a new library that is designed to make it easier to do high-performance I/O in .NET. It’s a
library targeting .NET Standard that works on all .NET implementations.

What problem does System.IO.Pipelines solve


Apps that parse streaming data are composed of boilerplate code having many specialized and unusual code flows.
The boilerplate and special case code is complex and difficult to maintain.
System.IO.Pipelines was architected to:
Have high performance parsing streaming data.
Reduce code complexity.
The following code is typical for a TCP server that receives line-delimited messages (delimited by '\n' ) from a
client:

async Task ProcessLinesAsync(NetworkStream stream)


{
var buffer = new byte[1024];
await stream.ReadAsync(buffer, 0, buffer.Length);

// Process a single line from the buffer


ProcessLine(buffer);
}

The preceding code has several problems:


The entire message (end of line) might not be received in a single call to ReadAsync .
It's ignoring the result of stream.ReadAsync . stream.ReadAsync returns how much data was read.
It doesn't handle the case where multiple lines are read in a single ReadAsync call.
It allocates a byte array with each read.
To fix the preceding problems, the following changes are required:
Buffer the incoming data until a new line is found.
Parse all the lines returned in the buffer.
It's possible that the line is bigger than 1 KB (1024 bytes). The code needs to resize the input buffer until the
delimiter is found in order to fit the complete line inside the buffer.
If the buffer is resized, more buffer copies are made as longer lines appear in the input.
To reduce wasted space, compact the buffer used for reading lines.
Consider using buffer pooling to avoid allocating memory repeatedly.
The following code addresses some of these problems:
async Task ProcessLinesAsync(NetworkStream stream)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
var bytesBuffered = 0;
var bytesConsumed = 0;

while (true)
{
// Calculate the amount of bytes remaining in the buffer.
var bytesRemaining = buffer.Length - bytesBuffered;

if (bytesRemaining == 0)
{
// Double the buffer size and copy the previously buffered data into the new buffer.
var newBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length * 2);
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
// Return the old buffer to the pool.
ArrayPool<byte>.Shared.Return(buffer);
buffer = newBuffer;
bytesRemaining = buffer.Length - bytesBuffered;
}

var bytesRead = await stream.ReadAsync(buffer, bytesBuffered, bytesRemaining);


if (bytesRead == 0)
{
// EOF
break;
}

// Keep track of the amount of buffered bytes.


bytesBuffered += bytesRead;
var linePosition = -1;

do
{
// Look for a EOL in the buffered data.
linePosition = Array.IndexOf(buffer, (byte)'\n', bytesConsumed,
bytesBuffered - bytesConsumed);

if (linePosition >= 0)
{
// Calculate the length of the line based on the offset.
var lineLength = linePosition - bytesConsumed;

// Process the line.


ProcessLine(buffer, bytesConsumed, lineLength);

// Move the bytesConsumed to skip past the line consumed (including \n).
bytesConsumed += lineLength + 1;
}
}
while (linePosition >= 0);
}
}

The previous code is complex and doesn't address all the problems identified. High-performance networking
usually means writing very complex code to maximize performance. System.IO.Pipelines was designed to make
writing this type of code easier.

Pipe
The Pipe class can be used to create a PipeWriter/PipeReader pair. All data written into the PipeWriter is available
in the PipeReader :
var pipe = new Pipe();
PipeReader reader = pipe.Reader;
PipeWriter writer = pipe.Writer;

Pipe basic usage

async Task ProcessLinesAsync(Socket socket)


{
var pipe = new Pipe();
Task writing = FillPipeAsync(socket, pipe.Writer);
Task reading = ReadPipeAsync(pipe.Reader);

await Task.WhenAll(reading, writing);


}

async Task FillPipeAsync(Socket socket, PipeWriter writer)


{
const int minimumBufferSize = 512;

while (true)
{
// Allocate at least 512 bytes from the PipeWriter.
Memory<byte> memory = writer.GetMemory(minimumBufferSize);
try
{
int bytesRead = await socket.ReceiveAsync(memory, SocketFlags.None);
if (bytesRead == 0)
{
break;
}
// Tell the PipeWriter how much was read from the Socket.
writer.Advance(bytesRead);
}
catch (Exception ex)
{
LogError(ex);
break;
}

// Make the data available to the PipeReader.


FlushResult result = await writer.FlushAsync();

if (result.IsCompleted)
{
break;
}
}

// By completing PipeWriter, tell the PipeReader that there's no more data coming.
await writer.CompleteAsync();
}

async Task ReadPipeAsync(PipeReader reader)


{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;

while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))


{
// Process the line.
ProcessLine(line);
}

// Tell the PipeReader how much of the buffer has been consumed.
reader.AdvanceTo(buffer.Start, buffer.End);
reader.AdvanceTo(buffer.Start, buffer.End);

// Stop reading if there's no more data coming.


if (result.IsCompleted)
{
break;
}
}

// Mark the PipeReader as complete.


await reader.CompleteAsync();
}

bool TryReadLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)


{
// Look for a EOL in the buffer.
SequencePosition? position = buffer.PositionOf((byte)'\n');

if (position == null)
{
line = default;
return false;
}

// Skip the line + the \n.


line = buffer.Slice(0, position.Value);
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
return true;
}

There are two loops:


FillPipeAsync reads from the Socket and writes to the PipeWriter .
ReadPipeAsync reads from the PipeReader and parses incoming lines.

There are no explicit buffers allocated. All buffer management is delegated to the PipeReader and PipeWriter
implementations. Delegating buffer management makes it easier for consuming code to focus solely on the
business logic.
In the first loop:
PipeWriter.GetMemory(Int32) is called to get memory from the underlying writer.
PipeWriter.Advance(Int32) is called to tell the PipeWriter how much data was written to the buffer.
PipeWriter.FlushAsync is called to make the data available to the PipeReader .

In the second loop, the PipeReader consumes the buffers written by PipeWriter . The buffers come from the
socket. The call to PipeReader.ReadAsync :
Returns a ReadResult that contains two important pieces of information:
The data that was read in the form of ReadOnlySequence<byte> .
A boolean IsCompleted that indicates if the end of data (EOF ) has been reached.

After finding the end of line (EOL ) delimiter and parsing the line:
The logic processes the buffer to skip what's already processed.
PipeReader.AdvanceTo is called to tell the PipeReader how much data has been consumed and examined.

The reader and writer loops end by calling Complete . Complete lets the underlying Pipe release the memory it
allocated.
Backpressure and flow control
Ideally, reading and parsing work together:
The writing thread consumes data from the network and puts it in buffers.
The parsing thread is responsible for constructing the appropriate data structures.
Typically, parsing takes more time than just copying blocks of data from the network:
The reading thread gets ahead of the parsing thread.
The reading thread has to either slow down or allocate more memory to store the data for the parsing thread.
For optimal performance, there's a balance between frequent pauses and allocating more memory.
To solve the preceding problem, the Pipe has two settings to control the flow of data:
PauseWriterThreshold: Determines how much data should be buffered before calls to FlushAsync pause.
ResumeWriterThreshold: Determines how much data the reader has to observe before calls to
PipeWriter.FlushAsync resume.

PipeWriter.FlushAsync:
Returns an incomplete ValueTask<FlushResult> when the amount of data in the Pipe crosses
PauseWriterThreshold .
Completes ValueTask<FlushResult> when it becomes lower than ResumeWriterThreshold .

Two values are used to prevent rapid cycling, which can occur if one value is used.
Examples

// The Pipe will start returning incomplete tasks from FlushAsync until
// the reader examines at least 5 bytes.
var options = new PipeOptions(pauseWriterThreshold: 10, resumeWriterThreshold: 5);
var pipe = new Pipe(options);

PipeScheduler
Typically when using async and await , asynchronous code resumes on either on a TaskScheduler or on the
current SynchronizationContext.
When doing I/O, it's important to have fine-grained control over where the I/O is performed. This control allows
taking advantage of CPU caches effectively. Efficient caching is critical for high-performance apps like web servers.
PipeScheduler provides control over where asynchronous callbacks run. By default:
The current SynchronizationContext is used.
If there's no SynchronizationContext , it uses the thread pool to run callbacks.
public static void Main(string[] args)
{
var writeScheduler = new SingleThreadPipeScheduler();
var readScheduler = new SingleThreadPipeScheduler();

// Tell the Pipe what schedulers to use and disable the SynchronizationContext.
var options = new PipeOptions(readerScheduler: readScheduler,
writerScheduler: writeScheduler,
useSynchronizationContext: false);
var pipe = new Pipe(options);
}

// This is a sample scheduler that async callbacks on a single dedicated thread.


public class SingleThreadPipeScheduler : PipeScheduler
{
private readonly BlockingCollection<(Action<object> Action, object State)> _queue =
new BlockingCollection<(Action<object> Action, object State)>();
private readonly Thread _thread;

public SingleThreadPipeScheduler()
{
_thread = new Thread(DoWork);
_thread.Start();
}

private void DoWork()


{
foreach (var item in _queue.GetConsumingEnumerable())
{
item.Action(item.State);
}
}

public override void Schedule(Action<object> action, object state)


{
_queue.Add((action, state));
}
}

PipeScheduler.ThreadPool is the PipeScheduler implementation that queues callbacks to the thread pool.
PipeScheduler.ThreadPool is the default and generally the best choice. PipeScheduler.Inline can cause unintended
consequences such as deadlocks.
Pipe reset
It's frequently efficient to reuse the Pipe object. To reset the pipe, call PipeReader Reset when both the
PipeReader and PipeWriter are complete.

PipeReader
PipeReader manages memory on the caller's behalf. Always call PipeReader.AdvanceTo after calling
PipeReader.ReadAsync. This lets the PipeReader know when the caller is done with the memory so that it can be
tracked. The ReadOnlySequence<byte> returned from PipeReader.ReadAsync is only valid until the call the
PipeReader.AdvanceTo . It's illegal to use ReadOnlySequence<byte> after calling PipeReader.AdvanceTo .

PipeReader.AdvanceTo takes two SequencePosition arguments:


The first argument determines how much memory was consumed.
The second argument determines how much of the buffer was observed.
Marking data as consumed means that the pipe can return the memory to the underlying buffer pool. Marking
data as observed controls what the next call to PipeReader.ReadAsync does. Marking everything as observed means
that the next call to PipeReader.ReadAsync won't return until there's more data written to the pipe. Any other value
will make the next call to PipeReader.ReadAsync return immediately with the observed and unobserved data, but
data that has already been consumed.
Read streaming data scenarios
There are a couple of typical patterns that emerge when trying to read streaming data:
Given a stream of data, parse a single message.
Given a stream of data, parse all available messages.
The following examples use the TryParseMessage method for parsing messages from a ReadOnlySequence<byte> .
TryParseMessage parses a single message and update the input buffer to trim the parsed message from the buffer.
TryParseMessage is not part of .NET, it's a user written method used in the following sections.

bool TryParseMessage(ref ReadOnlySequence<byte> buffer, out Message message);

Read a single message


The following code reads a single message from a PipeReader and returns it to the caller.
async ValueTask<Message> ReadSingleMessageAsync(PipeReader reader,
CancellationToken cancellationToken = default)
{
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

// In the event that no message is parsed successfully, mark consumed


// as nothing and examined as the entire buffer.
SequencePosition consumed = buffer.Start;
SequencePosition examined = buffer.End;

try
{
if (TryParseMessage(ref buffer, out Message message))
{
// A single message was successfully parsed so mark the start as the
// parsed buffer as consumed. TryParseMessage trims the buffer to
// point to the data after the message was parsed.
consumed = buffer.Start;

// Examined is marked the same as consumed here, so the next call


// to ReadSingleMessageAsync will process the next message if there's
// one.
examined = consumed;

return message;
}

// There's no more data to be processed.


if (result.IsCompleted)
{
if (buffer.Length > 0)
{
// The message is incomplete and there's no more data to process.
throw new InvalidDataException("Incomplete message.");
}

break;
}
}
finally
{
reader.AdvanceTo(consumed, examined);
}
}

return null;
}

The preceding code:


Parses a single message.
Updates the consumed SequencePosition and examined SequencePosition to point to the start of the trimmed
input buffer.
The two SequencePosition arguments are updated because TryParseMessage removes the parsed message from
the input buffer. Generally, when parsing a single message from the buffer, the examined position should be one of
the following:
The end of the message.
The end of the received buffer if no message was found.
The single message case has the most potential for errors. Passing the wrong values to examined can result in an
out of memory exception or an infinite loop. For more information, see the PipeReader common problems section
in this article.
Reading multiple messages
The following code reads all messages from a PipeReader and calls ProcessMessageAsync on each.

async Task ProcessMessagesAsync(PipeReader reader, CancellationToken cancellationToken = default)


{
try
{
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

try
{
// Process all messages from the buffer, modifying the input buffer on each
// iteration.
while (TryParseMessage(ref buffer, out Message message))
{
await ProcessMessageAsync(message);
}

// There's no more data to be processed.


if (result.IsCompleted)
{
if (buffer.Length > 0)
{
// The message is incomplete and there's no more data to process.
throw new InvalidDataException("Incomplete message.");
}
break;
}
}
finally
{
// Since all messages in the buffer are being processed, you can use the
// remaining buffer's Start and End position to determine consumed and examined.
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
finally
{
await reader.CompleteAsync();
}
}

Cancellation
PipeReader.ReadAsync :
Supports passing a CancellationToken.
Throws an OperationCanceledException if the CancellationToken is canceled while there's a read pending.
Supports a way to cancel the current read operation via PipeReader.CancelPendingRead, which avoids raising
an exception. Calling PipeReader.CancelPendingRead causes the current or next call to PipeReader.ReadAsync to
return a ReadResult with IsCanceled set to true . This can be useful for halting the existing read loop in a non-
destructive and non-exceptional way.
private PipeReader reader;

public MyConnection(PipeReader reader)


{
this.reader = reader;
}

public void Abort()


{
// Cancel the pending read so the process loop ends without an exception.
reader.CancelPendingRead();
}

public async Task ProcessMessagesAsync()


{
try
{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;

try
{
if (result.IsCanceled)
{
// The read was canceled. You can quit without reading the existing data.
break;
}

// Process all messages from the buffer, modifying the input buffer on each
// iteration.
while (TryParseMessage(ref buffer, out Message message))
{
await ProcessMessageAsync(message);
}

// There's no more data to be processed.


if (result.IsCompleted)
{
break;
}
}
finally
{
// Since all messages in the buffer are being processed, you can use the
// remaining buffer's Start and End position to determine consumed and examined.
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
finally
{
await reader.CompleteAsync();
}
}

PipeReader common problems


Passing the wrong values to consumed or examined may result in reading already read data.
Passing buffer.End as examined may result in:
Stalled data
Possibly an eventual Out of Memory (OOM ) exception if data isn't consumed. For example,
PipeReader.AdvanceTo(position, buffer.End) when processing a single message at a time from the buffer.
Passing the wrong values to consumed or may result in an infinite loop. For example,
examined
PipeReader.AdvanceTo(buffer.Start) if buffer.Start hasn't changed will cause the next call to
PipeReader.ReadAsync to return immediately before new data arrives.

Passing the wrong values to consumed or examined may result in infinite buffering (eventual OOM ).
Using the ReadOnlySequence<byte> after calling PipeReader.AdvanceTo may result in memory corruption (use
after free).
Failing to call PipeReader.Complete/CompleteAsync may result in a memory leak.
Checking ReadResult.IsCompleted and exiting the reading logic before processing the buffer results in data
loss. The loop exit condition should be based on ReadResult.Buffer.IsEmpty and ReadResult.IsCompleted .
Doing this incorrectly could result in an infinite loop.
Problematic code
Data loss
The ReadResult can return the final segment of data when IsCompleted is set to true . Not reading that data
before exiting the read loop will result in data loss.

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> dataLossBuffer = result.Buffer;

if (result.IsCompleted)
{
break;
}

Process(ref dataLossBuffer, out Message message);

reader.AdvanceTo(dataLossBuffer.Start, dataLossBuffer.End);
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

Infinite loop
The following logic may result in an infinite loop if the Result.IsCompleted is true but there's never a complete
message in the buffer.

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.
Environment.FailFast("This code is terrible, don't use it!");
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> infiniteLoopBuffer = result.Buffer;
if (result.IsCompleted && infiniteLoopBuffer.IsEmpty)
{
break;
}

Process(ref infiniteLoopBuffer, out Message message);

reader.AdvanceTo(infiniteLoopBuffer.Start, infiniteLoopBuffer.End);
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

Here's another piece of code with the same problem. It's checking for a non-empty buffer before checking
ReadResult.IsCompleted . Because it's in an else if , it will loop forever if there's never a complete message in the
buffer.

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> infiniteLoopBuffer = result.Buffer;

if (!infiniteLoopBuffer.IsEmpty)
{
Process(ref infiniteLoopBuffer, out Message message);
}
else if (result.IsCompleted)
{
break;
}

reader.AdvanceTo(infiniteLoopBuffer.Start, infiniteLoopBuffer.End);
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

Unexpected Hang
Unconditionally calling PipeReader.AdvanceTo with buffer.End in the examined position may result in hangs when
parsing a single message. The next call to PipeReader.AdvanceTo won't return until:
There's more data written to the pipe.
And the new data wasn't previously examined.

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> hangBuffer = result.Buffer;

Process(ref hangBuffer, out Message message);

if (result.IsCompleted)
{
break;
}

reader.AdvanceTo(hangBuffer.Start, hangBuffer.End);

if (message != null)
{
return message;
}
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

Out of Memory (OOM )


With the following conditions, the following code keeps buffering until an OutOfMemoryException occurs:
There's no maximum message size.
The data returned from the PipeReader doesn't make a complete message. For example, it doesn't make a
complete message because the other side is writing a large message (For example, a 4-GB message).

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.
Environment.FailFast("This code is terrible, don't use it!");
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> thisCouldOutOfMemory = result.Buffer;

Process(ref thisCouldOutOfMemory, out Message message);

if (result.IsCompleted)
{
break;
}

reader.AdvanceTo(thisCouldOutOfMemory.Start, thisCouldOutOfMemory.End);

if (message != null)
{
return message;
}
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

Memory Corruption
When writing helpers that read the buffer, any returned payload should be copied before calling Advance . The
following example will return memory that the Pipe has discarded and may reuse it for the next operation
(read/write).

WARNING
Do NOT use the following code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The following sample is provided to explain PipeReader Common problems.

public class Message


{
public ReadOnlySequence<byte> CorruptedPayload { get; set; }
}
Environment.FailFast("This code is terrible, don't use it!");
Message message = null;

while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

ReadHeader(ref buffer, out int length);

if (length <= buffer.Length)


{
message = new Message
{
// Slice the payload from the existing buffer
CorruptedPayload = buffer.Slice(0, length)
};

buffer = buffer.Slice(length);
}

if (result.IsCompleted)
{
break;
}

reader.AdvanceTo(buffer.Start, buffer.End);

if (message != null)
{
// This code is broken since reader.AdvanceTo() was called with a position *after* the buffer
// was captured.
break;
}
}

return message;
}

WARNING
Do NOT use the preceding code. Using this sample will result in data loss, hangs, security issues and should NOT be copied.
The preceding sample is provided to explain PipeReader Common problems.

PipeWriter
The PipeWriter manages buffers for writing on the caller's behalf. PipeWriter implements IBufferWriter<byte> .
IBufferWriter<byte> makes it possible to get access to buffers to perform writes without additional buffer copies.
async Task WriteHelloAsync(PipeWriter writer, CancellationToken cancellationToken = default)
{
// Request at least 5 bytes from the PipeWriter.
Memory<byte> memory = writer.GetMemory(5);

// Write directly into the buffer.


int written = Encoding.ASCII.GetBytes("Hello".AsSpan(), memory.Span);

// Tell the writer how many bytes were written.


writer.Advance(written);

await writer.FlushAsync(cancellationToken);
}

The previous code:


Requests a buffer of at least 5 bytes from the PipeWriter using GetMemory.
Writes bytes for the ASCII string "Hello" to the returned Memory<byte> .
Calls Advance to indicate how many bytes were written to the buffer.
Flushes the PipeWriter , which sends the bytes to the underlying device.
The previous method of writing uses the buffers provided by the PipeWriter . Alternatively, PipeWriter.WriteAsync:
Copies the existing buffer to the PipeWriter .
Calls GetSpan , Advance as appropriate and calls FlushAsync.

async Task WriteHelloAsync(PipeWriter writer, CancellationToken cancellationToken = default)


{
byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");

// Write helloBytes to the writer, there's no need to call Advance here


// (Write does that).
await writer.WriteAsync(helloBytes, cancellationToken);
}

Cancellation
FlushAsync supports passing a CancellationToken. Passing a CancellationToken results in an
OperationCanceledException if the token is canceled while there's a flush pending. PipeWriter.FlushAsync supports
a way to cancel the current flush operation via PipeWriter.CancelPendingFlush without raising an exception.
Calling PipeWriter.CancelPendingFlush causes the current or next call to PipeWriter.FlushAsync or
PipeWriter.WriteAsync to return a FlushResult with IsCanceled set to true . This can be useful for halting the
yielding flush in a non-destructive and non-exceptional way.
PipeWriter common problems
GetSpan and GetMemory return a buffer with at least the requested amount of memory. Don't assume exact
buffer sizes.
There's no guarantee that successive calls will return the same buffer or the same-sized buffer.
A new buffer must be requested after calling Advance to continue writing more data. The previously acquired
buffer can't be written to.
Calling GetMemory or GetSpan while there's an incomplete call to FlushAsync isn't safe.
Calling Complete or CompleteAsync while there's unflushed data can result in memory corruption.

IDuplexPipe
The IDuplexPipe is a contract for types that support both reading and writing. For example, a network connection
would be represented by an IDuplexPipe .
Unlike Pipe which contains a PipeReader and a PipeWriter , IDuplexPipe represents a single side of a full duplex
connection. That means what is written to the PipeWriter will not be read from the PipeReader .

Streams
When reading or writing stream data, you typically read data using a de-serializer and write data using a serializer.
Most of these read and write stream APIs have a Stream parameter. To make it easier to integrate with these
existing APIs, PipeReader and PipeWriter expose an AsStream. AsStream returns a Stream implementation
around the PipeReader or PipeWriter .
Work with Buffers in .NET
9 minutes to read • Edit Online

This article provides an overview of types that help read data that runs across multiple buffers. They're primarily
used to support PipeReader objects.

IBufferWriter<T>
System.Buffers.IBufferWriter<T> is a contract for synchronous buffered writing. At the lowest level, the interface:
Is basic and not difficult to use.
Allows access to a Memory<T> or Span<T>. The Memory<T> or Span<T> can be written to and you can
determine how many T items were written.

void WriteHello(IBufferWriter<byte> writer)


{
// Request at least 5 bytes.
Span<byte> span = writer.GetSpan(5);
ReadOnlySpan<char> helloSpan = "Hello".AsSpan();
int written = Encoding.ASCII.GetBytes(helloSpan, span);

// Tell the writer how many bytes were written.


writer.Advance(written);
}

The preceding method:


Requests a buffer of at least 5 bytes from the IBufferWriter<byte> using GetSpan(5) .
Writes bytes for the ASCII string "Hello" to the returned Span<byte> .
Calls IBufferWriter<T> to indicate how many bytes were written to the buffer.
This method of writing uses the Memory<T> / Span<T> buffer provided by the IBufferWriter<T> . Alternatively, the
Write extension method can be used to copy an existing buffer to the IBufferWriter<T> . Write does the work of
calling GetSpan / Advance as appropriate, so there's no need to call Advance after writing:

void WriteHello(IBufferWriter<byte> writer)


{
byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");

// Write helloBytes to the writer. There's no need to call Advance here


// since Write calls Advance.
writer.Write(helloBytes);
}

ArrayBufferWriter<T> is an implementation of IBufferWriter<T> whose backing store is a single contiguous array.


IBufferWriter common problems
GetSpan and GetMemory return a buffer with at least the requested amount of memory. Don't assume exact
buffer sizes.
There's no guarantee that successive calls will return the same buffer or the same-sized buffer.
A new buffer must be requested after calling Advance to continue writing more data. A previously acquired
buffer cannot be written to after Advance has been called.
ReadOnlySequence<T>

ReadOnlySequence<T> is a struct that can represent a contiguous or noncontiguous sequence of T . It can be


constructed from:
1. A T[]
2. A ReadOnlyMemory<T>
3. A pair of linked list node ReadOnlySequenceSegment<T> and index to represent the start and end position of
the sequence.
The third representation is the most interesting one as it has performance implications on various operations on
the ReadOnlySequence<T> :

REPRESENTATION OPERATION COMPLEXITY

T[] / ReadOnlyMemory<T> Length O(1)

T[] / ReadOnlyMemory<T> GetPosition(long) O(1)

T[] / ReadOnlyMemory<T> Slice(int, int) O(1)

T[] / ReadOnlyMemory<T> Slice(SequencePostion, O(1)


SequencePostion)

ReadOnlySequenceSegment<T> Length O(1)

ReadOnlySequenceSegment<T> GetPosition(long) O(number of segments)

ReadOnlySequenceSegment<T> Slice(int, int) O(number of segments)

ReadOnlySequenceSegment<T> Slice(SequencePostion, O(1)


SequencePostion)

Because of this mixed representation, the ReadOnlySequence<T> exposes indexes as SequencePosition instead of an
integer. A SequencePosition :
Is an opaque value that represents an index into the ReadOnlySequence<T> where it originated.
Consists of two parts, an integer and an object. What these two values represent are tied to the implementation
of ReadOnlySequence<T> .
Access data
The ReadOnlySequence<T> exposes data as an enumerable of ReadOnlyMemory<T> . Enumerating each of the segments
can be done using a basic foreach:

long FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)


{
long position = 0;

foreach (ReadOnlyMemory<byte> segment in buffer)


{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return position + index;
}

position += span.Length;
}

return -1;
}

The preceding method searches each segment for a specific byte. If you need to keep track of each segment's
SequencePosition , ReadOnlySequence<T>.TryGet is more appropriate. The next sample changes the preceding
code to return a SequencePosition instead of an integer. Returning a SequencePosition has the benefit of allowing
the caller to avoid a second scan to get the data at a specific index.

SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)


{
SequencePosition position = buffer.Start;

while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))


{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return buffer.GetPosition(position, index);
}
}
return null;
}

The combination of SequencePosition and TryGet acts like an enumerator. The position field is modified at the
start of each iteration to be start of each segment within the ReadOnlySequence<T> .
The preceding method exists as an extension method on ReadOnlySequence<T> . PositionOf can be used to simplify
the preceding code:

SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data) => buffer.PositionOf(data);

Process a ReadOnlySequence<T>
Processing a ReadOnlySequence<T> can be challenging since data may be split across multiple segments within the
sequence. For the best performance, split code into two paths:
A fast path that deals with the single segment case.
A slow path that deals with the data split across segments.
There are a few approaches that can be used to process data in multi-segmented sequences:
Use the SequenceReader<T> .
Parse data segment by segment, keeping track of the SequencePosition and index within the segment parsed.
This avoids unnecessary allocations but may be inefficient, especially for small buffers.
Copy the ReadOnlySequence<T> to a contiguous array and treat it like a single buffer:
If the size of the ReadOnlySequence<T> is small, it may be reasonable to copy the data into a stack-
allocated buffer using the stackalloc operator.
Copy the ReadOnlySequence<T> into a pooled array using ArrayPool<T>.Shared.
Use ReadOnlySequence<T>.ToArray() . This isn't recommended in hot paths as it allocates a new T[] on
the heap.
The following examples demonstrate some common cases for processing ReadOnlySequence<byte> :
P r o c e ss b i n a r y d a t a

The following example parses a 4-byte big-endian integer length from the start of the ReadOnlySequence<byte> .

bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)


{
// If there's not enough space, the length can't be obtained.
if (buffer.Length < 4)
{
length = 0;
return false;
}

// Grab the first 4 bytes of the buffer.


var lengthSlice = buffer.Slice(buffer.Start, 4);
if (lengthSlice.IsSingleSegment)
{
// Fast path since it's a single segment.
length = BinaryPrimitives.ReadInt32BigEndian(lengthSlice.First.Span);
}
else
{
// There are 4 bytes split across multiple segments. Since it's so small, it
// can be copied to a stack allocated buffer. This avoids a heap allocation.
Span<byte> stackBuffer = stackalloc byte[4];
lengthSlice.CopyTo(stackBuffer);
length = BinaryPrimitives.ReadInt32BigEndian(stackBuffer);
}

// Move the buffer 4 bytes ahead.


buffer = buffer.Slice(lengthSlice.End);

return true;
}

P r o c e ss t e x t d a t a

The following example:


Finds the first newline ( \r\n ) in the ReadOnlySequence<byte> and returns it via the out 'line' parameter.
Trims that line, excluding the \r\n from the input buffer.
static bool TryParseLine(ref ReadOnlySequence<byte> buffer, out ReadOnlySequence<byte> line)
{
SequencePosition position = buffer.Start;
SequencePosition previous = position;
var index = -1;
line = default;

while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))


{
ReadOnlySpan<byte> span = segment.Span;

// Look for \r in the current segment.


index = span.IndexOf((byte)'\r');

if (index != -1)
{
// Check next segment for \n.
if (index + 1 >= span.Length)
{
var next = position;
if (!buffer.TryGet(ref next, out ReadOnlyMemory<byte> nextSegment))
{
// You're at the end of the sequence.
return false;
}
else if (nextSegment.Span[0] == (byte)'\n')
{
// A match was found.
break;
}
}
// Check the current segment of \n.
else if (span[index + 1] == (byte)'\n')
{
// It was found.
break;
}
}

previous = position;
}

if (index != -1)
{
// Get the position just before the \r\n.
var delimeter = buffer.GetPosition(index, previous);

// Slice the line (excluding \r\n).


line = buffer.Slice(buffer.Start, delimeter);

// Slice the buffer to get the remaining data after the line.
buffer = buffer.Slice(buffer.GetPosition(2, delimeter));
return true;
}

return false;
}

Em p t y se g m e n t s

It's valid to store empty segments inside of a ReadOnlySequence<T> . Empty segments may occur while enumerating
segments explicitly:
static void EmptySegments()
{
// This logic creates a ReadOnlySequence<byte> with 4 segments,
// two of which are empty.
var first = new BufferSegment(new byte[0]);
var last = first.Append(new byte[] { 97 })
.Append(new byte[0]).Append(new byte[] { 98 });

// Construct the ReadOnlySequence<byte> from the linked list segments.


var data = new ReadOnlySequence<byte>(first, 0, last, 1);

// Slice using numbers.


var sequence1 = data.Slice(0, 2);

// Slice using SequencePosition pointing at the empty segment.


var sequence2 = data.Slice(data.Start, 2);

Console.WriteLine($"sequence1.Length={sequence1.Length}"); // sequence1.Length=2
Console.WriteLine($"sequence2.Length={sequence2.Length}"); // sequence2.Length=2

// sequence1.FirstSpan.Length=1
Console.WriteLine($"sequence1.FirstSpan.Length={sequence1.FirstSpan.Length}");

// Slicing using SequencePosition will Slice the ReadOnlySequence<byte> directly


// on the empty segment!
// sequence2.FirstSpan.Length=0
Console.WriteLine($"sequence2.FirstSpan.Length={sequence2.FirstSpan.Length}");

// The following code prints 0, 1, 0, 1.


SequencePosition position = data.Start;
while (data.TryGet(ref position, out ReadOnlyMemory<byte> memory))
{
Console.WriteLine(memory.Length);
}
}

class BufferSegment : ReadOnlySequenceSegment<byte>


{
public BufferSegment(Memory<byte> memory)
{
Memory = memory;
}

public BufferSegment Append(Memory<byte> memory)


{
var segment = new BufferSegment(memory)
{
RunningIndex = RunningIndex + Memory.Length
};
Next = segment;
return segment;
}
}

The preceding code creates a ReadOnlySequence<byte> with empty segments and shows how those empty segments
affect the various APIs:
ReadOnlySequence<T>.Slice with a SequencePosition pointing to an empty segment preserves that segment.
ReadOnlySequence<T>.Slice with an int skips over the empty segments.
Enumerating the ReadOnlySequence<T> enumerates the empty segments.
Potential problems with ReadOnlySequence<T> and SequencePosition
There are several unusual outcomes when dealing with a ReadOnlySequence<T> / SequencePosition vs. a normal
ReadOnlySpan<T> / ReadOnlyMemory<T> / T[] / int :
SequencePosition is a position marker for a specific ReadOnlySequence<T> , not an absolute position. Because it's
relative to a specific ReadOnlySequence<T> , it doesn't have meaning if used outside of the ReadOnlySequence<T>
where it originated.
Arithmetic can't be performed on SequencePosition without the ReadOnlySequence<T> . That means doing basic
things like position++ is written ReadOnlySequence<T>.GetPosition(position, 1) .
GetPosition(long) does not support negative indexes. That means it's impossible to get the second to last
character without walking all segments.
Two SequencePosition can't be compared, making it difficult to:
Know if one position is greater than or less than another position.
Write some parsing algorithms.
ReadOnlySequence<T> is bigger than an object reference and should be passed by in or ref where possible.
Passing ReadOnlySequence<T> by in or ref reduces copies of the struct.
Empty segments:
Are valid within a ReadOnlySequence<T> .
Can appear when iterating using the ReadOnlySequence<T>.TryGet method.
Can appear slicing the sequence using the ReadOnlySequence<T>.Slice() method with SequencePosition
objects.

SequenceReader<T>
SequenceReader<T>:
Is a new type that was introduced in .NET Core 3.0 to simplify the processing of a ReadOnlySequence<T> .
Unifies the differences between a single segment ReadOnlySequence<T> and multi-segment ReadOnlySequence<T>
.
Provides helpers for reading binary and text data ( byte and char ) that may or may not be split across
segments.
There are built-in methods for dealing with processing both binary and delimited data. The following section
demonstrates what those same methods look like with the SequenceReader<T> :
Access data
SequenceReader<T> has methods for enumerating data inside of the ReadOnlySequence<T> directly. The following
code is an example of processing a ReadOnlySequence<byte> a byte at a time:

while (reader.TryRead(out byte b))


{
Process(b);
}

The CurrentSpan exposes the current segment's Span , which is similar to what was done in the method manually.
Use position
The following code is an example implementation of FindIndexOf using the SequenceReader<T> :
SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)
{
var reader = new SequenceReader<byte>(buffer);

while (!reader.End)
{
// Search for the byte in the current span.
var index = reader.CurrentSpan.IndexOf(data);
if (index != -1)
{
// It was found, so advance to the position.
reader.Advance(index);

return reader.Position;
}
// Skip the current segment since there's nothing in it.
reader.Advance(reader.CurrentSpan.Length);
}

return null;
}

Process binary data


The following example parses a 4-byte big-endian integer length from the start of the ReadOnlySequence<byte> .

bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)


{
var reader = new SequenceReader<byte>(buffer);
return reader.TryReadBigEndian(out length);
}

Process text data

static ReadOnlySpan<byte> NewLine => new byte[] { (byte)'\r', (byte)'\n' };

static bool TryParseLine(ref ReadOnlySequence<byte> buffer,


out ReadOnlySequence<byte> line)
{
var reader = new SequenceReader<byte>(buffer);

if (reader.TryReadTo(out line, NewLine))


{
buffer = buffer.Slice(reader.Position);

return true;
}

line = default;
return false;
}

SequenceReader<T> common problems


Because SequenceReader<T> is a mutable struct, it should always be passed by reference.
SequenceReader<T> is a ref struct so it can only be used in synchronous methods and can't be stored in fields.
For more information, see Write safe and efficient C# code.
SequenceReader<T> is optimized for use as a forward-only reader. Rewind is intended for small backups that
can't be addressed utilizing other Read , Peek , and IsNext APIs.
Memory-Mapped Files
9 minutes to read • Edit Online

A memory-mapped file contains the contents of a file in virtual memory. This mapping between a file and memory
space enables an application, including multiple processes, to modify the file by reading and writing directly to the
memory. Starting with the .NET Framework 4, you can use managed code to access memory-mapped files in the
same way that native Windows functions access memory-mapped files, as described in Managing Memory-
Mapped Files.
There are two types of memory-mapped files:
Persisted memory-mapped files
Persisted files are memory-mapped files that are associated with a source file on a disk. When the last
process has finished working with the file, the data is saved to the source file on the disk. These memory-
mapped files are suitable for working with extremely large source files.
Non-persisted memory-mapped files
Non-persisted files are memory-mapped files that are not associated with a file on a disk. When the last
process has finished working with the file, the data is lost and the file is reclaimed by garbage collection.
These files are suitable for creating shared memory for inter-process communications (IPC ).

Processes, Views, and Managing Memory


Memory-mapped files can be shared across multiple processes. Processes can map to the same memory-mapped
file by using a common name that is assigned by the process that created the file.
To work with a memory-mapped file, you must create a view of the entire memory-mapped file or a part of it. You
can also create multiple views to the same part of the memory-mapped file, thereby creating concurrent memory.
For two views to remain concurrent, they have to be created from the same memory-mapped file.
Multiple views may also be necessary if the file is greater than the size of the application’s logical memory space
available for memory mapping (2 GB on a 32-bit computer).
There are two types of views: stream access view and random access view. Use stream access views for sequential
access to a file; this is recommended for non-persisted files and IPC. Random access views are preferred for
working with persisted files.
Memory-mapped files are accessed through the operating system’s memory manager, so the file is automatically
partitioned into a number of pages and accessed as needed. You do not have to handle the memory management
yourself.
The following illustration shows how multiple processes can have multiple and overlapping views to the same
memory-mapped file at the same time.
The following image shows multiple and overlapped views to a memory-mapped file:
Programming with Memory-Mapped Files
The following table provides a guide for using memory-mapped file objects and their members.

TASK METHODS OR PROPERTIES TO USE

To obtain a MemoryMappedFile object that represents a MemoryMappedFile.CreateFromFile method.


persisted memory-mapped file from a file on disk.

To obtain a MemoryMappedFile object that represents a non- MemoryMappedFile.CreateNew method.


persisted memory-mapped file (not associated with a file on
disk). - or -

MemoryMappedFile.CreateOrOpen method.

To obtain a MemoryMappedFile object of an existing memory- MemoryMappedFile.OpenExisting method.


mapped file (either persisted or non-persisted).

To obtain a UnmanagedMemoryStream object for a MemoryMappedFile.CreateViewStream method.


sequentially accessed view to the memory-mapped file.

To obtain a UnmanagedMemoryAccessor object for a random MemoryMappedFile.CreateViewAccessor method.


access view to a memory-mapped fie.

To obtain a SafeMemoryMappedViewHandle object to use MemoryMappedFile.SafeMemoryMappedFileHandle property.


with unmanaged code.
- or -

MemoryMappedViewAccessor.SafeMemoryMappedViewHandl
e property.

- or -

MemoryMappedViewStream.SafeMemoryMappedViewHandle
property.
TASK METHODS OR PROPERTIES TO USE

To delay allocating memory until a view is created (non- CreateNew method with the
persisted files only). MemoryMappedFileOptions.DelayAllocatePages value.

(To determine the current system page size, use the - or -


Environment.SystemPageSize property.)
CreateOrOpen methods that have a
MemoryMappedFileOptions enumeration as a parameter.

Security
You can apply access rights when you create a memory-mapped file, by using the following methods that take a
MemoryMappedFileAccess enumeration as a parameter:
MemoryMappedFile.CreateFromFile
MemoryMappedFile.CreateNew
MemoryMappedFile.CreateOrOpen
You can specify access rights for opening an existing memory-mapped file by using the OpenExisting methods that
take an MemoryMappedFileRights as a parameter.
In addition, you can include a MemoryMappedFileSecurity object that contains predefined access rules.
To apply new or changed access rules to a memory-mapped file, use the SetAccessControl method. To retrieve
access or audit rules from an existing file, use the GetAccessControl method.

Examples
Persisted Memory-Mapped Files
The CreateFromFile methods create a memory-mapped file from an existing file on disk.
The following example creates a memory-mapped view of a part of an extremely large file and manipulates a
portion of it.
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
static void Main(string[] args)
{
long offset = 0x10000000; // 256 megabytes
long length = 0x20000000; // 512 megabytes

// Create the memory-mapped file.


using (var mmf = MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data",
FileMode.Open,"ImgA"))
{
// Create a random access view, from the 256th megabyte (the offset)
// to the 768th megabyte (the offset plus length).
using (var accessor = mmf.CreateViewAccessor(offset, length))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;

// Make changes to the view.


for (long i = 0; i < length; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(10);
accessor.Write(i, ref color);
}
}
}
}
}

public struct MyColor


{
public short Red;
public short Green;
public short Blue;
public short Alpha;

// Make the view brighter.


public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices

Class Program

Sub Main()
Dim offset As Long = &H10000000 ' 256 megabytes
Dim length As Long = &H20000000 ' 512 megabytes

' Create the memory-mapped file.


Using mmf = MemoryMappedFile.CreateFromFile("c:\ExtremelyLargeImage.data", FileMode.Open, "ImgA")
' Create a random access view, from the 256th megabyte (the offset)
' to the 768th megabyte (the offset plus length).
Using accessor = mmf.CreateViewAccessor(offset, length)
Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
Dim color As MyColor
Dim i As Long = 0

' Make changes to the view.


Do While (i < length)
accessor.Read(i, color)
color.Brighten(10)
accessor.Write(i, color)
i += colorSize
Loop
End Using
End Using
End Sub
End Class

Public Structure MyColor


Public Red As Short
Public Green As Short
Public Blue As Short
Public Alpha As Short

' Make the view brighter.


Public Sub Brighten(ByVal value As Short)
Red = CType(Math.Min(Short.MaxValue, (CType(Red, Integer) + value)), Short)
Green = CType(Math.Min(Short.MaxValue, (CType(Green, Integer) + value)), Short)
Blue = CType(Math.Min(Short.MaxValue, (CType(Blue, Integer) + value)), Short)
Alpha = CType(Math.Min(Short.MaxValue, (CType(Alpha, Integer) + value)), Short)
End Sub
End Structure

The following example opens the same memory-mapped file for another process.
using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
static void Main(string[] args)
{
// Assumes another process has created the memory-mapped file.
using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
{
using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;

// Make changes to the view.


for (long i = 0; i < 1500000; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(20);
accessor.Write(i, ref color);
}
}
}
}
}

public struct MyColor


{
public short Red;
public short Green;
public short Blue;
public short Alpha;

// Make the view brigher.


public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices

Class Program
Public Shared Sub Main(ByVal args As String())
' Assumes another process has created the memory-mapped file.
Using mmf = MemoryMappedFile.OpenExisting("ImgA")
Using accessor = mmf.CreateViewAccessor(4000000, 2000000)
Dim colorSize As Integer = Marshal.SizeOf(GetType(MyColor))
Dim color As MyColor

' Make changes to the view.


Dim i As Long = 0
While i < 1500000
accessor.Read(i, color)
color.Brighten(30)
accessor.Write(i, color)
i += colorSize
End While
End Using
End Using
End Sub
End Class

Public Structure MyColor


Public Red As Short
Public Green As Short
Public Blue As Short
Public Alpha As Short

' Make the view brigher.


Public Sub Brighten(ByVal value As Short)
Red = CShort(Math.Min(Short.MaxValue, CInt(Red) + value))
Green = CShort(Math.Min(Short.MaxValue, CInt(Green) + value))
Blue = CShort(Math.Min(Short.MaxValue, CInt(Blue) + value))
Alpha = CShort(Math.Min(Short.MaxValue, CInt(Alpha) + value))
End Sub
End Structure

Non-Persisted Memory-Mapped Files


The CreateNew and CreateOrOpen methods create a memory-mapped file that is not mapped to an existing file
on disk.
The following example consists of three separate processes (console applications) that write Boolean values to a
memory-mapped file. The following sequence of actions occur:
1. Process A creates the memory-mapped file and writes a value to it.
2. Process B opens the memory-mapped file and writes a value to it.
3. Process C opens the memory-mapped file and writes a value to it.
4. Process A reads and displays the values from the memory-mapped file.
5. After Process A is finished with the memory-mapped file, the file is immediately reclaimed by garbage
collection.
To run this example, do the following:
1. Compile the applications and open three Command Prompt windows.
2. In the first Command Prompt window, run Process A .
3. In the second Command Prompt window, run Process B .
4. Return to Process A and press ENTER.
5. In the third Command Prompt window, run Process C .
6. Return to Process A and press ENTER.
The output of Process A is as follows:

Start Process B and press ENTER to continue.


Start Process C and press ENTER to continue.
Process A says: True
Process B says: False
Process C says: True

Process A

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process A:
static void Main(string[] args)
{
using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 10000))
{
bool mutexCreated;
Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();

Console.WriteLine("Start Process B and press ENTER to continue.");


Console.ReadLine();

Console.WriteLine("Start Process C and press ENTER to continue.");


Console.ReadLine();

mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
Console.WriteLine("Process A says: {0}", reader.ReadBoolean());
Console.WriteLine("Process B says: {0}", reader.ReadBoolean());
Console.WriteLine("Process C says: {0}", reader.ReadBoolean());
}
mutex.ReleaseMutex();
}
}
}
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1

' Process A:
Sub Main()
Using mmf As MemoryMappedFile = MemoryMappedFile.CreateNew("testmap", 10000)
Dim mutexCreated As Boolean
Dim mTex As Mutex = New Mutex(True, "testmapmutex", mutexCreated)
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(1)
End Using
mTex.ReleaseMutex()
Console.WriteLine("Start Process B and press ENTER to continue.")
Console.ReadLine()

Console.WriteLine("Start Process C and press ENTER to continue.")


Console.ReadLine()

mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream()
Dim reader As BinaryReader = New BinaryReader(Stream)
Console.WriteLine("Process A says: {0}", reader.ReadBoolean())
Console.WriteLine("Process B says: {0}", reader.ReadBoolean())
Console.WriteLine("Process C says: {0}", reader.ReadBoolean())
End Using
mTex.ReleaseMutex()

End Using

End Sub

End Module

Process B
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process B:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{

Mutex mutex = Mutex.OpenExisting("testmapmutex");


mutex.WaitOne();

using (MemoryMappedViewStream stream = mmf.CreateViewStream(1, 0))


{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(0);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run Process A first.");
}
}
}

Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1
' Process B:
Sub Main()
Try
Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(1, 0)
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(0)
End Using
mTex.ReleaseMutex()
End Using
Catch noFile As FileNotFoundException
Console.WriteLine("Memory-mapped file does not exist. Run Process A first." & vbCrLf &
noFile.Message)
End Try

End Sub

End Module

Process C
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process C:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap"))
{

Mutex mutex = Mutex.OpenExisting("testmapmutex");


mutex.WaitOne();

using (MemoryMappedViewStream stream = mmf.CreateViewStream(2, 0))


{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B.");
}
}
}

Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Threading

Module Module1
' Process C:
Sub Main()
Try
Using mmf As MemoryMappedFile = MemoryMappedFile.OpenExisting("testmap")
Dim mTex As Mutex = Mutex.OpenExisting("testmapmutex")
mTex.WaitOne()
Using Stream As MemoryMappedViewStream = mmf.CreateViewStream(2, 0)
Dim writer As BinaryWriter = New BinaryWriter(Stream)
writer.Write(1)
End Using
mTex.ReleaseMutex()
End Using
Catch noFile As FileNotFoundException
Console.WriteLine("Memory-mapped file does not exist. Run Process A first, then B." & vbCrLf &
noFile.Message)
End Try

End Sub

End Module

See also
File and Stream I/O

S-ar putea să vă placă și