FAQ: Where Do I Save Files, and How Exactly Do I Do That?

The correct ways to identify folder paths to store files depends on the programming technology you use. This blog post shows how to do it in C++, C# and VB. NET, PowerShell, Windows Script Host (VBScript and JScript), and as a last resort, environment variables.

One of the more common programming mistakes that lead to compatibility problems is the incorrect specification of folder paths in programs.  For example, it is not uncommon for programs to assume that the user’s profile is under “C:\Documents and Settings”.  These default paths are always subject to change and have changed across different versions of Windows.  User profiles were stored under %SystemDrive%\WINNT\Profiles, then under “%SystemDrive%\Documents and Settings”, and now under %SystemDrive%\Users. The “All Users” profile is now called “Public”, and what was in “%USERPROFILE%\Local Settings\Application Data” is now in “%USERPROFILE%\AppData\Local”. Also, part of what used to be under the “All Users” profile is now in a separate folder location (by default, C:\ProgramData).

How can programs work correctly when these paths keep changing?  Well, applications that are written correctly don’t require any modification to get the correct folder locations on all versions of Windows. Some rules you should follow:

  • Do not hardcode any file system paths.
  • Do not assume that Windows is installed on the C: drive, that there is a “Documents and Settings” or a “Users” folder, or a “Program Files” folder.
  • DO use symbolic constants and Windows APIs or environment variables to identify the appropriate place to put file content.
  • Distinguish between per-user and shared content.
  • Distinguish between files users should be able to browse in Explorer (e.g., documents that users create) and files that aren’t intended for direct access by users (application configuration settings, for example). These file types get stored in different locations.

Here are the correct ways to identify folder locations using a variety of programming technologies:

C++

Use the SHGetSpecialFolderPath function with CSIDL constants. For programs designed to run only on Windows Vista or newer, use the SHGetKnownFolderPath function with KNOWNFOLDERID constants. See the following links:

These two examples demonstrate retrieving the paths for the current user’s Documents folder and the computer’s shared Documents folder:

 

HRESULT hr;

TCHAR szPath[MAX_PATH];

hr = SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, szPath);

if (SUCCEEDED(hr))

{

    ...

}

 

hr = SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, szPath);

if (SUCCEEDED(hr))

{

    ...

}

C#/VB .NET (using Managed code)

Use the Environment.GetFolderPath method, passing in a Environment.SpecialFolder enumeration. The System.IO.Path.Combine method can be used to combine path parts. For example, the following C# code returns the path to a “MyData” subfolder in the current user’s Documents folder:

 

string sPath;

sPath = System.IO.Path.Combine(

    Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),

    "MyData");

 

This is the same code implemented in VB .NET:

 

Dim sPath As String

sPath = System.IO.Path.Combine( _

    Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), _

    "MyData")

References:

 

PowerShell

Windows PowerShell is built on .NET and can invoke many .NET methods and resources from the PowerShell command line interface or from Notepad-editable script files. This is how to implement the earlier sample building a path to a “MyData” subfolder of the user’s Documents folder using PowerShell:

 

$sPath = [System.IO.Path]::Combine(

    [Environment]::GetFolderPath([Environment+SpecialFolder]::MyDocuments),

    "MyData")

 

C#/VB .NET Using Native Methods (P/Invoke)

You can invoke the native Windows SHGetFolderPath API from C# or VB .NET code using Platform Invoke (a.k.a., P/Invoke) methods. See https://pinvoke.net/default.aspx/shell32/SHGetFolderPath.html for examples. This may be useful because the native CSIDL enumeration includes many more folder locations than the .NET SpecialFolder enumeration did before .NET v4.

 

Windows Script Host – JScript and VBScript

Windows Script Host defines a SpecialFolders collection that can be used from VBScript or JScript. The following JScript example outputs the path to the common (“all users”) desktop:

 

    var oWsh = WScript.CreateObject("WScript.Shell");

    var sDesk = oWsh.SpecialFolders("AllUsersDesktop");

    WScript.Echo(sDesk);

 

And here is the same code in VBScript:

 

    Dim oWsh, sDesk

    Set oWsh = WScript.CreateObject("WScript.Shell")

    sDesk = oWsh.SpecialFolders("AllUsersDesktop")

    WScript.Echo sDesk

 

References:

Environment Variables and Batch Files

If none of the above interfaces are available (for example, from a Cmd.exe batch file), Windows defines a relatively small number of environment variables that identify some file system locations. Using these environment variables, at least for partial paths, is better than hardcoding paths. The following table lists the filepath-related environment variables on my Windows 7 SP1 x64 system and their values. Note that these are just example values as found on a particular computer. Do not assume that the same path locations are the same on other computers. See the References for the meanings and intended purposes of these variables.

Environment variable name

Example value

ALLUSERSPROFILE

C:\ProgramData

APPDATA

C:\Users\username\AppData\Roaming

CommonProgramFiles

C:\Program Files\Common Files

CommonProgramFiles(x86)

C:\Program Files (x86)\Common Files

CommonProgramW6432

C:\Program Files\Common Files

ComSpec

C:\Windows\system32\cmd.exe

HOMEDRIVE

C:

HOMEPATH

\Users\username

LOCALAPPDATA

C:\Users\username\AppData\Local

ProgramData

C:\ProgramData

ProgramFiles

C:\Program Files

ProgramFiles(x86)

C:\Program Files (x86)

ProgramW6432

C:\Program Files

PSModulePath

C:\Windows\system32\WindowsPowerShell\v1.0\Modules\

PUBLIC

C:\Users\Public

SystemDrive

C:

SystemRoot

C:\Windows

TEMP

C:\Users\username\AppData\Local\Temp

TMP

C:\Users\username\AppData\Local\Temp

USERPROFILE

C:\Users\username

windir

C:\Windows

 

References: