The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,830 other subscribers

Since more than 40 years, `c:tmp.txt` is a valid file path on Windows (because of MS-DOS 2.x)

Posted by jpluimers on 2026/07/22

Some are surprised but because of MS-DOS 2.x compatibility:

`c:tmp.txt` is a valid file path on Windows

Since each drive letter remembers the current directory and that memory bit is not thread-local nor process-local, so relying on it is bad practice.

The above is a quote from [Wayback/Archive] Oleksii Holub 🇺🇦 on Twitter: “So I was trying to understand the difference between <code>Path.IsPathRooted(...)</code> and <code>Path.IsPathFullyQualified(...)</code> and learned that apparently <code>c:tmp.txt</code> is a valid file path on Windows 🤯” which has an image without alt-text on a long bit of Microsoft documentation which I will further quote below.

Many people don’t know this, so here is a list of variations on relative paths:

path meaning
C:temp.txt relative to the current directory on the specific drive C: *
temp.txt relative to the current directory on the current drive *
\temp.txt relative to the current drive inside its root directory

Paths can only be relative on drives and even the drive letter can be relative.

Note that for * you can also prefix temp.txt with directory names, for instance:

path meaning
.\ the current directory
..\ the parent directory of the current directory
zzz\ a specific directory zzz inside the current directory

This means there are no relative paths on:

  • UNC paths (often called network paths)
  • Namespace paths (either \\?\X:\ based where X: is a drive, or \\.\DeviceName\ based though the latter are not available from cmd.exe)

From a historic perspective, this is very logical as back in the days, these were the main APIs to change drive and current directory, and to retrieve those values (all from the famous Ralf Brown’s Interrupt List – Wikipedia):

This has resulted in some run-time libraries still distinguishing between ChDrive (or ChDrv) and ChDir.

Related

Old-new-Thing entries:

Notes

Because cmd.exe stores the current directory for each drive in a separate environment variable, but most Windows API functions do not, you might want to do that yourself. This is exactly what the C++ RTL _chdir does in addition to the calling the SetCurrentDirectory functionality as per [Wayback/Archive] _chdrive / SetCurrentDirectory resetting the current directory on other drives?

I always forget the CRT source is available. :)

The most interesting source is that of _chdir, which shows it is doing
extra bookkeeping beyond what SetCurrentDirectory does, as I suspected
(although the way the extra info is stored and the fact that other
Win32 APIs use it automatically did surprise me).

From chdir.c:

* If the new current directory path is NOT a UNC path, we must
* update the OS environment variable specifying the current
* directory for what is now current drive. To do this, get the
* full current directory, build the environment variable string
* and call SetEnvironmentVariable(). We need to do this because
* SetCurrentDirectory does not (i.e., does not update the
* current-directory-on-drive environment variables) and other
* functions (fullpath, spawn, etc) need them to be set.
*
* If associated with a 'drive', the current directory should
* have the form of the example below:
*
* D:\nt\private\mytests
*
* so that the environment variable should be of the form:
*
* =D:=D:\nt\private\mytests

Quotes

Few people seem to know [Wayback/Archive] Changing the Current Directory – Win32 apps | Microsoft Learn

Although each process can have only one current directory, if the application switches volumes by using the SetCurrentDirectory function, the system remembers the last current path for each volume (drive letter). This behavior will manifest itself only when specifying a drive letter without a fully qualified path when changing the current directory point of reference to a different volume. This applies to either Get or Set operations.

Which means that unlike cmd.exe (which uses Environment variables to keep of the current directory for each drive), there is no need for PowerShell to do the same. So dir env: does not show these environment variables: [Wayback/Archive] PowerTip: Use Windows PowerShell to display all Environment variables – Scripting Blog

Answer: You can do this in one line using the env: PowerShell drive to display all of the currently set Environment variables.
dir env:

In addition, the current directory is a proces wide thing, and can cause thread-safety issues as per [Wayback/Archive] SetCurrentDirectory function (winbase.h) – Win32 apps | Microsoft Learn:

The current directory is shared by all threads of the process: If one thread changes the current directory, it affects all threads in the process. Multithreaded applications and shared library code should avoid calling the SetCurrentDirectory function due to the risk of affecting relative path calculations being performed by other threads. Conversely, multithreaded applications and shared library code should avoid using relative paths so that they are unaffected by changes to the current directory performed by other threads.

Quoted bit of information of [Wayback/Archive] Naming Files, Paths, and Namespaces – Win32 apps | Microsoft Learn:

Fully Qualified vs. Relative Paths

For Windows API functions that manipulate files, file names can often be relative to the current directory, while some APIs require a fully qualified path. A file name is relative to the current directory if it does not begin with one of the following:

  • A UNC name of any format, which always start with two backslash characters (“\\“). For more information, see the next section.
  • A disk designator with a backslash, for example “C:\” or “d:\“.
  • A single backslash, for example, “\directory” or “\file.txt“. This is also referred to as an absolute path.

If a file name begins with only a disk designator but not the backslash after the colon, it is interpreted as a relative path to the current directory on the drive with the specified letter. Note that the current directory may or may not be the root directory depending on what it was set to during the most recent “change directory” operation on that disk. Examples of this format are as follows:

  • “C:tmp.txt” refers to a file named “tmp.txt” in the current directory on drive C.
  • “C:tempdir\tmp.txt” refers to a file in a subdirectory to the current directory on drive C.

A path is also said to be relative if it contains “double-dots”; that is, two periods together in one component of the path. This special specifier is used to denote the directory above the current directory, otherwise known as the “parent directory”. Examples of this format are as follows:

  • “..\tmp.txt” specifies a file named tmp.txt located in the parent of the current directory.
  • “..\..\tmp.txt” specifies a file that is two directories above the current directory.
  • “..\tempdir\tmp.txt” specifies a file named tmp.txt located in a directory named tempdir that is a peer directory to the current directory.

Relative paths can combine both example types, for example “C:..\tmp.txt”. This is useful because, although the system keeps track of the current drive along with the current directory of that drive, it also keeps track of the current directories in each of the different drive letters (if your system has more than one), regardless of which drive designator is set as the current drive.

Delphi?

Don’t count on the Delphi RTL to get this right: [Wayback/Archive] Meik Tranel on Twitter: “@jpluimers @Tyrrrz Now the question is – is TPath.IsPathRooted following this as well or does it actually check what i’m referring to? Because there is no FullyQualified check in System.IOUtils.TPath… “

Queries

–jeroen

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.