[WINAPI] GetOpenFileName/GetSaveFileName 会改变程序的当前工作目录,小心使用
如果不是出了问题,可能谁也不会想到为什么 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 恢复当前工作目录。