微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用函数 GetFileAttributesA(Windows) 时,C++ 会查看哪个目录

如何解决使用函数 GetFileAttributesA(Windows) 时,C++ 会查看哪个目录

我有一个目录 A,其中有一个文件 someFile.ext,我在终端中 cd 到目录 A。在目录 BI 中有一个 tmp.exe,我从目录 A 运行 tmp.exe。现在在 tmp.exe代码中,我有一个函数检查文件 someFile.ext 是否存在或不使用该函数GetFileAttributesA(如果不返回 INVALID_FILE_ATTRIBUTES,则文件存在)。现在我想知道当我调用函数 GetFileAttributesA("someFile.ext") 时它在哪个目录中查找,在目录 A 还是目录 B 中?请注意,我不只提供文件名的完整路径。对于 Visual Studios 中的 32 位和 64 位体系结构,此更改行为是否发生更改。

解决方法

简短的回答是,当给定相对路径时,所有 Windows 文件 API 都相对于进程的当前目录。

当您运行一个程序时,该程序(最初)从父进程继承当前目录。进程可以更改其当前目录。

以上应该足以回答您的问题。


但 Windows 实际上比许多人意识到的要复杂一些,因为一个进程实际上有一个当前驱动器和一个每个驱动器的当前目录。所以进程的“当前工作目录”就是它当前驱动器的当前目录。

  • C:\foo\a.txt 是特定文件的绝对路径。
  • foo\a.txt 是相对路径,相对于进程的“当前工作目录”(即当前驱动器的当前目录)。
  • D:a.txt 也是一个相对路径,但它相对于 D 驱动器的进程当前目录,即使进程的当前驱动器不是 D
  • \foo\a.txt 可能看起来像一个绝对路径,因为进程的当前目录不相关,但它实际上是一个相对路径,因为它相对于进程当前驱动器的根目录。

每个驱动器的目录皱纹不适合许多文件系统抽象模型,尤其是在从 Posix 平台移植的代码中。

还有更多奇特的文件系统,比如 VMS/RMS,它有一个“默认文件规范”而不是“当前工作目录”。很多人认为它们是同一枚硬币的两面,但它们在概念上是截然不同的野兽。


更新: 评论者指出了 Raymond Chen 的一篇博客文章,其中讨论了 CMD.EXE 如何创建每个驱动器当前目录的错觉。确实,CMD 设置了使用特殊环境变量跟踪各种当前目录的错觉。 Win32 的 GetCurrentDirectory 也只跟踪一个当前目录。

然而,其余的 Win32 文件 API 尊重并使用 CMD 设置的那些特殊环境变量来解析非绝对路径,因此即使在 Win32 API 级别,如果您的进程继承了魔法来自 CMD 的环境变量,结果就像每个驱动器都有一个单独的当前目录。

请允许我举例说明。我们将从使用 GetFileAttributesW 确定文件 "D:a.txt" 是否存在的程序开始。我已经对文件名进行了硬编码,以确保我们使用的正是那个字符串。在其他操作系统中,shell 通常在将参数传递给程序之前解析命令行中的文件规范。

C:\Users\Adrian McCarthy\source>type test.cpp
#include <Windows.h>
#include <iostream>

int main() {
    const WCHAR *file_path = L"D:a.txt";
    const auto attr = ::GetFileAttributesW(file_path);
    std::wcout << L'"' << file_path << L"\" was "
               << ((attr == INVALID_FILE_ATTRIBUTES) ? L"not " : L"")
               << L"found." << std::endl;
    return 0;
}

C:\Users\Adrian McCarthy\source>cl /nologo /EHsc /W4 /WX test.cpp
test.cpp

如果我将当前目录设置为D驱动器的根目录(这里没有a.txt),然后切换回C驱动器运行程序,我们确认没有这样的文件。

C:\Users\Adrian McCarthy\source>d:

D:\>c:

C:\Users\Adrian McCarthy\source>test
"D:a.txt" was not found.

但是如果我将驱动器 D 的当前目录更改为 \foo,我确实有一个 a.txt,那么即使程序不知道,同样的命令也会找到该文件foo 目录中,没有硬编码,也不是来自“官方”当前工作目录。

C:\Users\Adrian McCarthy\source>d:

D:\>cd foo

D:\foo>dir
 Volume in drive D is Data
 Volume Serial Number is D631-E371

 Directory of D:\foo

07/21/2021  09:49 AM    <DIR>          .
07/21/2021  09:49 AM    <DIR>          ..
07/21/2021  09:49 AM                24 a.txt
               1 File(s)             24 bytes
               2 Dir(s)  1,491,949,572,096 bytes free

D:\foo>c:

C:\Users\Adrian McCarthy\source>test
"D:a.txt" was found.

唯一可行的方法是 Win32 文件 API 尊重 CMD 设置的魔法环境设置和继承的测试过程。

这很重要,因为许多“可移植”项目使用文件系统抽象层,尝试显式解析相对路径,而不是让底层系统 API 访问它。

如果他们确定 D:a.txt 是绝对路径,并且在通往 WinAPI 的路上不理会它,它会起作用。但是如果他们认为 D:a.txt 是一个相对路径,那么他们会向系统询问当前的工作目录并尝试将它与相对路径合并,然后提出一些格式错误的或不存在的绝对路径。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。