Windows has a set of reserved filenames that cannot be used for files or folders. These names are reserved for legacy reasons, dating back to DOS. If you try to create a file or folder with one of these names, you will get an error.
The list of reserved names is:
CON, PRN, AUX, NULCOM0 through COM9LPT0 through LPT9
These names come from DOS, where devices were exposed as special file-like names. For example, PRN mapped to the default printer, COM1 to COM9 mapped to serial ports used by modems and terminals, and LPT1 to LPT9 mapped to parallel printer ports. CON represented the console (keyboard and screen) and NUL discarded all input, and Windows still reserves all these names for backward compatibility. For instance, you can print a document using copy /b document.txt PRN.
These names are case-insensitive, so con, Con, and CON are all reserved. Also, you cannot use these names with an extension. For example, NUL.txt is also invalid.
##Win32 API vs. File System
By default, Win32 path parsing (used by Windows Explorer, PowerShell, .NET, etc.) denies creating such files or folders. The file system itself (NTFS, FAT32, ReFS) does not enforce this specific restriction. This means you can still create and manipulate these names through interfaces that bypass or change Win32 parsing, such as WSL (Windows Subsystem for Linux), or by using Win32 APIs with the \\?\ prefix.
##Creating reserved files with WSL
If you have WSL installed, you can easily create a file with a reserved name.
Shell
touch NUL
If you try to access this file from Windows Explorer, you may encounter errors or unexpected behavior.
##Accessing reserved files in .NET
If you try to access a file named NUL using the standard System.IO APIs in .NET, you will get an exception.
C#
var file = "c:\\sample\\NUL";
// Throws System.IO.IOException: The parameter is incorrect. : '\\.\NUL'
Console.WriteLine(new FileInfo(file).Length);
The exception message is:
Unhandled exception. System.IO.IOException: The parameter is incorrect. : '\\.\NUL'.
at System.IO.FileSystemInfo.EnsureDataInitialized()
at System.IO.FileInfo.get_Length()
at Program.<Main>$(String[] args) in Program.cs:line 11
It's important to note that Path.GetInvalidFileNameChars() returns a list of invalid characters for file and folder names, but it does not include these reserved names. So, you cannot rely on this method to validate if a filename is valid on Windows.
##Bypassing the restriction
You can bypass the default Win32 path restriction by using the \\?\ prefix. This prefix tells the Windows APIs to disable most legacy path parsing and pass the path more directly to the file system driver.
In practice, this matters because reserved names such as CON, PRN, AUX, NUL, COM1, or LPT1 are mostly enforced by the Win32 compatibility layer. Without \\?\, Win32 interprets these names as DOS device aliases before the request reaches the file system. With \\?\, this interpretation step is skipped, so names like c:\sample\NUL can be treated as regular path components by NTFS.
The prefix does not bypass security checks or file permissions. It bypasses only legacy path parsing rules.
C#
// Write to NUL device (does not create a file, acts like /dev/null)
File.WriteAllText("NUL", "");
// Bypass the check and create a file named NUL on the disk
File.WriteAllText(@"\\?\c:\sample\NUL", "test");
Using \\?\ allows you to create files that are otherwise impossible to create or delete using standard Windows tools. If you have such a file and want to delete it, you can also use the \\?\ prefix:
C#
File.Delete(@"\\?\c:\sample\NUL");
#Meziantou.Framework.FullPath
The Meziantou.Framework.FullPath NuGet package provides a FullPath type that simplifies working with file and directory paths. It also supports creating and manipulating files with reserved names on Windows. By default, it uses the \\?\ prefix internally on Windows when the value contains reserved names, allowing you to work with reserved filenames seamlessly.
C#
using Meziantou.Framework.FullPath;
var filePath = FullPath.Parse(@"c:\sample\NUL");
File.WriteAllText(filePath, "test");
Console.WriteLine(File.ReadAllText(filePath)); // Outputs: test
##Additional resources
Do you have a question or a suggestion about this post? Contact me!