File path formats on Windows systems - .NET (2024)

  • Article

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.

PathDescription
C:\Documents\Newsletters\Summer2018.pdfAn absolute file path from the root of drive C:.
\Program Files\Custom Utilities\StringFinder.exeA relative path from the root of the current drive.
2018\January.xlsxA relative path to a file in a subdirectory of the current directory.
..\Publications\TravelBrochure.pdfA relative path to a file in a directory starting from the current directory.
C:\Projects\apilibrary\apilibrary.slnAn absolute path to a file from the root of drive C:.
C:Projects\apilibrary\apilibrary.slnA 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, if the path is independent of the current directory and does not change when the current directory changes) by calling the Path.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:\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.DiagnosticsImports System.IOImports System.ReflectionPublic 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") 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 SubEnd 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:

PathDescription
\\system07\C$\The root directory of the C: drive on system07.
\\Server2\Share\Test\Foo.txtThe 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

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 .NET 5+ and in .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\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 and cannot begin with a relative directory segment (. or ..). 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.IOModule 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 SubEnd 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.

Identify 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.

Handle 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.

Apply 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.

Canonicalize 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.

Evaluate 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 \\.\).

Trim 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.

Skip 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 .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 and .NET 5+ handles long paths implicitly and does not perform a MAX_PATH check. The MAX_PATH check applies only to .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.IOModule Example Public Sub Main() Dim fi As New FileInfo(".\test.txt") fi.MoveTo(".\Test.txt") End SubEnd 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 uppercase and lowercase letters will match "test.txt".

File path formats on Windows systems - .NET (2024)

FAQs

What is the Windows system file path? ›

The Windows System PATH tells your PC where it can find specific directories that contain executable files. Ipconfig.exe, for example, is found in the C:\Windows\System32 directory, which is a part of the system PATH by default.

How to get file path in Windows application C#? ›

The following code snippet returns the full path of a file.
  1. string fullFileName = fi.FullName;
  2. Console.WriteLine("File Name: {0}", fullFileName);
Dec 20, 2018

What is the C$ path in Windows? ›

UNC paths. The $ sign in C$ indicates a hidden administrative network share, and is not a substition for the drive colon : , as the first version of this article claimed. The C$ -style drive shares are simply convenient shortcuts automatically created by Windows.

What do Windows uses for file path? ›

Windows traditionally uses the backslash ( \ ) to separate directories in file paths. (For example, C:\Program Files\PuppetLabs .) However, the Puppet language also uses the backslash ( \ ) as an escape character in quoted strings.

What directory contains all of the Windows operating system files? ›

If you're looking for the system files of your Windows OS, you'll probably find them in C:\Windows, usually in specific subfolders, like /System32 and /SysWOW64.

Where are system files stored? ›

The majority of system files for a Windows operating system are kept in the C:Windows folder, particularly in the subfolders /System32 and /SysWOW64. System files can also be found in user directories (like AppData) and program folders (like Program Data or Program Files).

How to get all file paths in a directory C#? ›

The simplest way to search directories and list files in C# is by using the Directory. GetFiles() method. This method returns an array of strings that represents the paths of all files in a specified directory that match a specified search pattern.

How to get specific folder path in C#? ›

GetDirectoryName(ReadOnlySpan<Char>)

Returns the directory information for the specified path represented by a character span.

What is the difference between path and directory? ›

(Alternate definition: A directory is a folder.) Path: this term is descriptive in that it represents a type of "road map" to a specific file or directory. (Alternate definition: A path is a list, beginning with a drive letter, that tells which folders to open so that you can find a file or another folder.)

What is a file path example? ›

Absolute file paths point to a directory or file by explicitly mentioning all its parent directories since the root. For example, on Windows, the Documents folder is typically stored on a hard drive called C:, inside a user-specific directory, and can usually be referenced by the file path C:/Users/username/Documents.

How to check file path? ›

To view the full path of an individual file:
  1. Click the Start button and then click Computer, click to open the location of the desired file, hold down the Shift key and right-click the file.
  2. On the menu, there are two options to choose from that will allow you to either copy or view the entire file path:
Jul 23, 2019

What are the two types of file paths? ›

File paths specify the location of individual files. They are used to give files access to one another and they are of two types : Absolute and Relative. Relative file paths on the hand points to the location of files in the root folder of an individual web project with reference to the current working file.

How to set path for C in Windows? ›

For Windows
  1. The first step depends which version of Windows you're using: ...
  2. Click "Advanced system settings".
  3. Click "Environment Variables".
  4. Under "System Variables", find the PATH variable, select it, and click "Edit". ...
  5. Add your directory to the beginning of the variable value followed by ; (a semicolon). ...
  6. Click "OK".
Sep 4, 2021

Where is the C:\Documents and Settings folder in Windows 10? ›

In Windows 10, no 'C:\Documents and Settings' folder anymore. You may find that folder's contents in 'C:\Users\YourUserID\AppData\Local' folder in Windows 10.

How to open C path in cmd? ›

Change Directory in CMD – Step-by-Step
  1. Step 1: Search CMD on Windows Search Bar. First, press the Windows key on your keyboard. ...
  2. Step 2: Open Command Prompt or Run as Administrator. Click on the Open button on the right. ...
  3. Step 3: Type 'cd' Next to Cursor. ...
  4. Step 4: Find the Path of Your Directory.
Mar 6, 2024

Top Articles
Latest Posts
Article information

Author: Carmelo Roob

Last Updated:

Views: 6195

Rating: 4.4 / 5 (65 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Carmelo Roob

Birthday: 1995-01-09

Address: Apt. 915 481 Sipes Cliff, New Gonzalobury, CO 80176

Phone: +6773780339780

Job: Sales Executive

Hobby: Gaming, Jogging, Rugby, Video gaming, Handball, Ice skating, Web surfing

Introduction: My name is Carmelo Roob, I am a modern, handsome, delightful, comfortable, attractive, vast, good person who loves writing and wants to share my knowledge and understanding with you.