在 Windows 下,C++ 可以通过 Win API 或者标准库来获取和设置当前目录,接下来将分别对其进行说明。

通过 WIN API 获取和设置当前目录

每个进程都有一个当前目录,它由两部分组成:

  • 磁盘指示符,可以是驱动器号后跟冒号,或服务器名称后跟共享名 (\\servername\sharename)
  • 磁盘指示符上的目录

多线程应用程序和共享库代码不应使用 GetCurrentDirectory 函数,并且应避免使用相对路径名。SetCurrentDirectory 函数写入的当前目录状态作为全局变量存储在每个进程中,因此,多线程应用程序无法可靠地使用此值,因为其他线程也可能正在读取或设置此值。此限制也适用于 SetCurrentDirectoryGetFullPathName 函数。

通过 WIN API 获取和设置当前目录的 API 如下

GetCurrentDirectory

GetCurrentDirectory 的函数原型如下:

DWORD GetCurrentDirectory(
DWORD nBufferLength,
LPTSTR lpBuffer
);
  • 参数
    • nBufferLength: 当前目录字符串的缓冲区长度,以 TCHAR 为单位。缓冲区长度必须包括终止空字符的空间
    • lpBuffer: 指向接收当前目录字符串的缓冲区的指针。这个以空字符结尾的字符串指定了当前目录的绝对路径。要确定所需的缓冲区大小,请将此参数设置为 NULL,并将 nBufferLength 参数设置为 0。
  • 返回值
    • 如果函数成功,则返回值指定写入缓冲区的字符数,不包括终止空字符
    • 如果函数失败,则返回值为零。要获取错误信息,请调用 GetLastError
    • 如果 lpBuffer 指向的缓冲区不够大,则返回值指定缓冲区所需的大小,以字符为单位,包括空终止字符

SetCurrentDirectory

SetCurrentDirectory 的函数原型如下:

BOOL SetCurrentDirectory(
LPCTSTR lpPathName
);
  • 参数
    • lpPathName: 新的当前目录的路径。此参数可以指定相对路径或完整路径。无论哪种情况,都会计算指定目录的完整路径并将其存储为当前目录。在此函数的 ANSI 版本中,名称限制为 MAX_PATH 个字符。空字符之前的最后一个字符必须是反斜杠 ('\')。如果你不指定反斜杠,它会为你添加;因此,除非包含尾部反斜杠,否则为路径指定 MAX_PATH-2 个字符,在这种情况下,为路径指定 MAX_PATH-1 个字符。
  • 返回值
    • 如果函数成功,则返回值非零
    • 如果函数失败,则返回值为零。要获取扩展错误信息,请调用 GetLastError

Example

#include <windows.h>
#include <iostream>
#include <format>

#define BUFSIZE MAX_PATH

std::wstring getCurrentDirectory()
{
TCHAR Buffer[BUFSIZE];
DWORD dwRet;

dwRet = GetCurrentDirectory(BUFSIZE, Buffer);
if (dwRet == 0)
{
std::wcout << std::format(L"GetCurrentDirectory failed. error code: {}\n", GetLastError());
return L"";
}

if (dwRet > BUFSIZE)
{
std::wcout << std::format(L"Buffer too small; need {} characters\n", dwRet);
return L"";
}

std::wcout << std::format(L"Current directory is {} \n", Buffer);

return Buffer;
}

int main()
{
auto oldCurDir = getCurrentDirectory();

std::wstring newDir = LR"(C:\ProgramData)";

if (!SetCurrentDirectory(newDir.c_str()))
{
std::wcout << std::format(L"SetCurrentDirectory failed. error code: {}", GetLastError());
return -1;
}
std::wcout << std::format(L"set current directory to {}\n", newDir);
auto CurDir = getCurrentDirectory();

if (!SetCurrentDirectory(oldCurDir.c_str()))
{
std::wcout << std::format(L"SetCurrentDirectory failed. error code: {}\n", GetLastError());
return -1;
}
std::wcout << std::format(L"Restore previous directory {}\n", oldCurDir);
CurDir = getCurrentDirectory();

return 0;
}

通过标准库获取和设置当前目录

标准库 std::filesystem 中有处理当前目录的函数,设置和获取当前目录的函数名相同,其函数原型如下:

path current_path();
path current_path( std::error_code& ec );
void current_path( const std::filesystem::path& p );
void current_path( const std::filesystem::path& p, std::error_code& ec ) noexcept;
#include <iostream>
#include <format>
#include <filesystem>

namespace fs = std::filesystem;

fs::path getCurrentDirectory()
{
std::error_code ec;
auto path = fs::current_path(ec);
if (ec)
{
std::cout << std::format("getCurrentDirectory failed. error: {}\n", ec.message());
return fs::path{};
}

return path;
}

bool setCurrentDirectory(const fs::path& path)
{
std::error_code ec;
fs::current_path(path, ec);
if (ec)
{
std::cout << std::format("setCurrentDirectory failed. error : {}\n", ec.message());
return false;
}

return true;
}

void testStdApi()
{
std::cout << std::format( "current directory: {}\n", getCurrentDirectory().string());
std::wstring newDir = LR"(C:\ProgramData)";
if (setCurrentDirectory(newDir))
{
std::cout << std::format("after set new directory.\n");
std::cout << std::format("current directory: {}\n", getCurrentDirectory().string());
}
}

int main()
{
testStdApi();
return 0;
}

程序输出为:

current directory: D:\VS_Projects\Utils
after set new directory.
current directory: C:\ProgramData