Skip to main content

Getting special folders (🟢 Beginner)

Quickly get the paths to your user’s Desktop, Home, or Program Files folder—cross-platform, in just a few lines of code!

Win32​

#include <shlobj.h>
#include <print>
#include <string>

constexpr int CSIDL_HOME = 0x0040; // The Home folder is not defined because it is not a standard CSIDL value.

std::string get_environment_variable(const std::string& variable) {
auto environent_variable_size = DWORD {32766};
auto environment_variable = std::string(environent_variable_size, '\0');
environent_variable_size = GetEnvironmentVariable(variable.data(), environment_variable.data(), environent_variable_size);
return environment_variable.substr(0, environent_variable_size);
}

std::string get_known_folder_path(int id) {
// For CSIDL_HOME, we construct the path using environment variables
if (id == CSIDL_HOME)
return get_environment_variable("HOMEDRIVE") + get_environment_variable("HOMEPATH");
// For other known folders, we use SHGetFolderPath
auto path = std::string(MAX_PATH, '\0');
if (SHGetFolderPath(nullptr, id, nullptr, SHGFP_TYPE_CURRENT, path.data()) != S_OK)
throw std::runtime_error("Failed to get known folder path");
return path.substr(0, path.find('\0'));
}

auto main() -> int {
std::println("Desktop = \"{}\"", get_known_folder_path(CSIDL_DESKTOP));
std::println("Program Files = \"{}\"", get_known_folder_path(CSIDL_PROGRAM_FILES));
std::println("Home = \"{}\"", get_known_folder_path(CSIDL_HOME));
}
// This code can produce the following output:
//
// Desktop = "C:\Users\gammasoft71\Desktop"
// Program Files = "C:\Program Files"
// Home = "C:\Users\gammasoft71"
  • This Win32 example demonstrates how to retrieve special folders by using system APIs.
  • For standard folders like Desktop or Program Files, we use SHGetFolderPath.
  • For Home, which doesn’t have a standard CSIDL, we construct the path from environment variables.

Qt​

#include <QApplication>
#include <QStandardPaths>
#include <QString>

#if defined(Q_OS_WIN)
#include <shlobj.h>
#pragma comment(lib, "shell32.lib")
#endif

QTextStream& qCout() {
static auto ts = QTextStream {stdout};
return ts;
}

QString programFilesPath() {
#if defined(Q_OS_WIN)
auto path = std::string(MAX_PATH, '\0');
if (SHGetFolderPath(nullptr, CSIDL_PROGRAM_FILES, nullptr, SHGFP_TYPE_CURRENT, path.data()) != S_OK)
throw std::runtime_error("Failed to get known folder path");
return path.substr(0, path.find('\0'));
#elif defined(Q_OS_MAC)
return "/Applications";
#else
return QDir("/opt").exists() ? "/opt" : "/usr/local";
#endif
}

auto main(int argc, char* argv[]) -> int {
auto application = QApplication {argc, argv};

qCout() << "Desktop = \"" << QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) << "\"" << Qt::endl;
qCout() << "Program Files = \"" << programFilesPath() << "\"" << Qt::endl;
qCout() << "Home = \"" << QStandardPaths::writableLocation(QStandardPaths::HomeLocation) << "\"" << Qt::endl;
}
// This code can produce the following output:
//
// Desktop = "C:\Users\gammasoft71\Desktop"
// Program Files = "C:\Program Files"
// Home = "C:\Users\gammasoft71"
  • This Qt example shows how to get special folders using the built-in QStandardPaths for Home and Desktop.
  • For Program Files, we still rely on a system call (SHGetFolderPath) on Windows, while macOS and Linux use conventional paths.
  • It illustrates that Qt simplifies some folders but isn’t fully unified across all platforms.

xtd​

#include <xtd/xtd>

auto main() -> int {
console::write_line("Desktop = {}", environment::get_folder_path(environment::special_folder::desktop).quoted());
console::write_line("Program Files = {}", environment::get_folder_path(environment::special_folder::program_files).quoted());
console::write_line("Home = {}", environment::get_folder_path(environment::special_folder::home).quoted());
}
// This code can produce the following output:
//
// Desktop = "C:\Users\gammasoft71\Desktop"
// Program Files = "C:\Program Files"
// Home = "C:\Users\gammasoft71"
  • This xtd example demonstrates how to retrieve special folders in a fully cross-platform way.
  • With just three lines of code, we can get Desktop, Home, or Program Files without any platform-specific calls or #ifdef logic.
  • It highlights xtd’s simplicity and unified API compared to Win32 and Qt.

Conclusion​

Whether you use Win32, Qt or xtd, it is always possible to get the special folders. The main difference lies in:

  • Win32 : verbose, sometimes deprecated API, manual management of special cases (such as Home).
  • Qt : simplifies some folders (Home, Desktop) but still requires system calls for others (Program Files).
  • Xtd : unified, cross-platform, clear and readable approach, without #ifdef or explicit native calls.

With xtd, the focus is on the essentials: writing code that works everywhere, without worrying about the implementation details specific to each OS.

Did you know?

  • On Windows, the Program Files folder can change depending on the system language or if the user has moved it.
  • On macOS, the concept of Program Files does not exist: the convention wants applications to be in /Applications.
  • On Linux, there is no strict equivalent: /opt and /usr/local are often used for third-party applications.
  • xtd::environment::get_folder_path automatically manages these particularities according to the platform.

To go further​

#include <xtd/xtd>

auto main() -> int {
console::write_line("My target file = {}", path::combine(environment::get_folder_path(environment::special_folder::home), "MyAppData", "settings.txt").quoted());
}

// This code can produce the following output on Windows:
// My target file = "C:\Users\gammasoft71\MyAppData\settings.txt"
//
// This code can produce the following output on macOS:
// My target file = "/Users/gammasoft71/MyAppData/settings.txt"
//
// This code can produce the following output on Linux:
// My target file = "/home/gammasoft71/MyAppData/settings.txt"

See also​