有没有一个函数可以获取 Windows 上文件的真实且区分大小写的路径?

问题描述 投票:0回答:1

我正在为游戏创建

std::filesystem
Lua 绑定,我想确保稍后使用该绑定的 mod 不依赖于 Windows 的大小写不敏感。

例如,在 Windows 上按

fopen("foo.txt", "w")
后按
fopen("Foo.txt", "r")
可以读取文件,而在 Linux 上则不会,因为 Linux 区分大小写。这不仅涉及文件名,还涉及整个路径,因为假设
bar/
存在,则
fopen("bar/baz.txt", "w")
后跟
fopen("Bar/baz.txt", "r")
在 Windows 上可以工作,但在 Linux 上也会失败。因此,虽然该 mod 可以在 Windows 上运行,但对于 Linux 用户来说它会崩溃,这是我想防止的。

之前,我通过使用代码“使”Linux 不区分大小写来解决这个问题,这实际上是通过迭代目录中的所有条目并检查名称是否匹配来构建一条路径,一次一个父目录不区分大小写。

这个解决方案的代码不是很优雅,而且相当大,现在我决定通过“使”Windows 区分大小写来做完全相反的事情,我希望也许有一些我可以使用的功能Windows 获取文件的真正区分大小写的路径,因此

fopen("Foo.txt", "r")
both 操作系统上都会失败。

我在谷歌上搜索了很多,并在SO上查找了以前问过这个问题的任何其他人,但大多数答案要么是尖锐的“为什么有人需要它”,要么执行与我上面链接的当前解决方案相同的操作。

我尝试使用

GetFullPathName()
例如将其放入
foo.cpp
文件中,但它打印
C:\Users\<user>\Desktop\Foo.cpp
,所以没有用,因为我需要
Foo.cpp
与实际文件名相同:

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

int main() {
    char filename[] = "Foo.cpp";
    char fullFilename[MAX_PATH];

    GetFullPathName(filename, MAX_PATH, fullFilename, nullptr);
    std::cout << fullFilename << std::endl;
}

谢谢

c++ windows
1个回答
0
投票

策略是打开文件并检索实际打开的文件路径(使用

GetFinalPathNameByHandle
):

struct HandleCloser {
    HANDLE h;
    ~HandleCloser() { ::CloseHandle(h); }
};

std::wstring final_path(const wchar_t* file_name) {
    HANDLE fp = ::CreateFileW(
        /*lpFileName=*/file_name,
        /*dwDesiredAccess=*/0,
        /*dwShareMode=*/FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
        /*lpSecurityAttributes=*/nullptr,
        /*dwCreationDisposition=*/OPEN_EXISTING,
        /*dwFlagsAndAttributes=*/FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OPEN_REPARSE_POINT,
        /*hTemplateFile=*/nullptr);
    // (FILE_FLAG_BACKUP_SEMANTICS to work on directories and
    // FILE_FLAG_OPEN_REPARSE_POINT to work on symlinks)
    if (fp == INVALID_HANDLE_VALUE) {
        // Handle error...
        return {};
    }
    HandleCloser _{fp};
    std::wstring result;
    result.resize(MAX_PATH);
    DWORD size = ::GetFinalPathNameByHandleW(
        fp, result.data(), result.size() + 1u, FILE_NAME_NORMALIZED
    );
    if (size > result.size()) {
        result.resize(size);
        size = ::GetFinalPathNameByHandleW(
            fp, result.data(), result.size() + 1u, FILE_NAME_NORMALIZED
        );
    }
    if (size == 0) {
        // Handle error...
        return {};
    }
    result.resize(size);
    return result;
}

不要使用

FILE_FLAG_POSIX_SEMANTICS
打开文件,这一点至关重要,因为这样就不会发生不区分大小写的路径转换。为此,您不需要文件的读/写权限。如果路径不存在,这将失败,在这种情况下,您可能需要遍历子目录以查看它们的大小写是否正确。

我认为这也是

std::filesystem::canonical
对 Microsoft STL 所做的事情。


或者,您可以在 Windows 上实现 Lua

fopen
,使用
FILE_FLAG_POSIX_SEMANTICS
打开。这将不允许您在写入
Foo.txt
时读取
foo.txt
,并允许您拥有两个不同的文件
Foo.txt
foo.txt
,即使在 Windows 上也是如此。

© www.soinside.com 2019 - 2024. All rights reserved.