兩大核心需求區分
| 需求 | API | 含義 | 調試陷阱 |
| 進程工作目錄 | GetCurrentDirectory | 進程啟動/運行時的「當前目錄」 (可被 chdir/SetCurrentDirectory 改變) | VS 調試時工作目錄 = 項目目錄,非 Debug/Release 輸出目錄! |
| 模塊(EXE/DLL)所在目錄 | GetModuleFileName | 可執行文件/加載模塊的完整路徑,提取目錄部分 | 動態加載 DLL 時獲取的是 DLL 路徑,需傳 NULL 獲取 EXE 路徑 |
方案一:獲取工作目錄 (C 風格 / MFC)
// C 風格 (需 )
#include
#include
std::string GetWorkingDir() {
char buffer[MAX_PATH];
DWORD len = GetCurrentDirectoryA(MAX_PATH, buffer);
if (len == 0 || len > MAX_PATH) return "";
return std::string(buffer, len);
}
// MFC CString 版
CString GetWorkingDirMFC() {
CString path;
GetCurrentDirectory(MAX_PATH, path.GetBuffer(MAX_PATH));
path.ReleaseBuffer();
return path;
}
方案二:獲取 EXE 所在目錄 (健壯、推薦)
// 通用版 (支持 Unicode TCHAR)
#include
#include
#include
#include // PathRemoveFileSpec
#pragma comment(lib, "shlwapi.lib")
std::wstring GetExeDir() {
wchar_t path[MAX_PATH];
// NULL 代表當前進程 EXE
DWORD len = GetModuleFileNameW(nullptr, path, MAX_PATH);
if (len == 0 || len == MAX_PATH) return L""; // 截斷需動態分配
PathRemoveFileSpecW(path); // 就地移除文件名,保留目錄 (含尾部反斜槓)
return std::wstring(path);
}
// 無 Shlwapi 手動版
std::wstring GetExeDirManual() {
wchar_t path[MAX_PATH];
GetModuleFileNameW(nullptr, path, MAX_PATH);
std::wstring ws(path);
size_t pos = ws.find_last_of(L"\\/");
if (pos != std::wstring::npos) ws.resize(pos + 1); // 保留分隔符
return ws;
}
關鍵技術細節
- Unicode 統一用 W 版 API:
GetModuleFileNameW、wchar_t、std::wstring,避免 ANSI 代碼頁亂碼。
- MAX_PATH 限制 (260):長路徑可能截斷。現代 Windows 10+ (版本 1607+) 支持長路徑,需清單啟用或用
\\?\ 前綴。安全做法:動態分配緩衝區 (std::vector 循環增大調用)。
PathRemoveFileSpec 就地修改:高效移除文件名,保留目錄分隔符。
- DLL 中獲取 EXE 路徑:
GetModuleFileNameW(nullptr, ...)。若需 DLL 自身路徑,傳 GetModuleHandleW(L"mydll.dll") 或 &__ImageBase (MSVC 特有)。
現代 C++17 跨平台方案:std::filesystem
#include
#include
namespace fs = std::filesystem;
int main() {
// 1. 當前工作目錄 (跨平台)
fs::path cwd = fs::current_path();
std::cout << "CWD: " << cwd << std::endl;
// 2. 可執行文件路徑 (C++20: std::filesystem::executable_path() / C++17 需平台特定)
// Windows:
wchar_t buf[MAX_PATH];
GetModuleFileNameW(nullptr, buf, MAX_PATH);
fs::path exePath = buf;
std::cout << "EXE Dir: " << exePath.parent_path() << std::endl;
// 3. 路徑操作 (拼接、刪除文件名、擴展名、規範化)
fs::path logDir = exePath.parent_path() / "logs";
fs::create_directories(logDir); // 遞歸創建
return 0;
}
總結建議
- 配置文件/數據文件讀寫:用 **EXE 所在目錄** (
GetModuleFileName / fs::executable_path),穩定不受工作目錄變更影響。
- 相對路徑處理/用戶交互:用 **工作目錄** (
GetCurrentDirectory / fs::current_path)。
- 新項目強制用
std::filesystem:跨平台、類型安全、路徑運算符重載、UTF-8/UTF-16 自動處理。
- 調試時設置工作目錄:VS 項目屬性 → 調試 → 工作目錄 設為
$(OutDir) 或數據目錄,使調試行為與發布一致。