[WINAPI] GetOpenFileName/GetSaveFileName 会改变程序的当前工作目录,小心使用

陪她去流浪 桃子 2016年05月13日 编辑 阅读次数:2976

如果不是出了问题,可能谁也不会想到为什么 GetOpenFileName/GetSaveFileName 会改变程序的当前工作目录。

很多程序在运行过程中都不会理会当前工作目录是啥,但是必须明白的是:当前工作目录是不能被删除的,会是占用状态

所以,就算你不管当前工作目录是啥,但请不要带来其它副作用。使用 GetOpenFileName/GetSaveFileName 就会。

我写了一个简单的例子来验证(其实无需再验证,因为我已经踩到此坑了):

#include <string>
#include <iostream>

#include <windows.h>

std::string get_file_name(bool open_or_save = true) {
    char path[MAX_PATH];
    OPENFILENAMEA ofn = {sizeof(ofn)};
    ofn.Flags = OFN_EXPLORER;
    path[0] = '\0';
    ofn.lpstrFile = &path[0];
    ofn.nMaxFile = _countof(path);

    return (open_or_save ? ::GetOpenFileName(&ofn) : ::GetSaveFileName(&ofn))
        ? path
        : "";
}

std::string get_current_directory() {
    char path[MAX_PATH];
    path[GetCurrentDirectory(_countof(path), &path[0])] = '\0';
    return path;
}

int main() {
    SetCurrentDirectory("C:\\");

    std::cout << "before GetOpenFileName    : " << get_current_directory()  << std::endl;
    std::cout << "GetOpenFileName           : " << get_file_name()          << std::endl;
    std::cout << "after GetOpenFileName     : " << get_current_directory()  << std::endl;

    std::cout << "--------------------------------------------------------" << std::endl;

    std::cout << "before GetSaveFileName    : " << get_current_directory()  << std::endl;
    std::cout << "GetSaveFileName           : " << get_file_name(false)     << std::endl;
    std::cout << "after GetSaveFileName     : " << get_current_directory()  << std::endl;

    std::cout << "--------------------------------------------------------" << std::endl;

    std::cout << "before GetOpenFileName    : " << get_current_directory()  << std::endl;
    std::cout << "GetOpenFileName           : " << get_file_name()          << std::endl;
    std::cout << "after GetOpenFileName     : " << get_current_directory()  << std::endl;

    return 0;
}

,以下是一个可能的结果:

before GetOpenFileName    : C:\
GetOpenFileName           : C:\Users\Tao\Desktop\celery.jpg
after GetOpenFileName     : C:\Users\Tao\Desktop
--------------------------------------------------------
before GetSaveFileName    : C:\Users\Tao\Desktop
GetSaveFileName           : E:\Downloads\highlight.zip
after GetSaveFileName     : E:\Downloads
--------------------------------------------------------
before GetOpenFileName    : E:\Downloads
GetOpenFileName           :
after GetOpenFileName     : E:\Downloads

前面两个都有选择目录文件,可以看到,在调用 GetOpenFileName/GetSaveFileName 前后,工作目录的确已经发生了改变。第3个没有选择文件(但还是切换了目录的),貌似没有改变。

说貌似是因为,这个改变与否不确定。我现在是 Win10,没有改变,但我之前的 Win7 是改变了的。这个坑也正是在 Win7 取消选择后发现的。


如果仔细读 MSDN 中的 OPENFILENAME 结构体的说明,它提到过一个标志位:

OFN_NOCHANGEDIR
0x00000008

Restores the current directory to its original value if the user changed the directory while searching for files.

This flag is ineffective for GetOpenFileName.

,可能谁有不会明白为什么会有这个标志位存在。好吧,我允许你的存在,但为什么不是默认?或者改成 OFN_CHANGEDIR?因为那没什么卵用。

“这个标志位对 GetOpenFileName 无效。”

所以,无论什么时候,你都应该在调用这两个函数之一前使用 GetCurrentDirectory 备份当前工作目录,调用之后使用 SetCurrentDirectory 恢复当前工作目录

标签:WinAPI