使用“MoveFileA”重命名文件夹,其中文件打开的文件句柄

Rename folder using `MoveFileA` with open file handles of the files inside

提问人:tearfur 提问时间:11/15/2023 最后编辑:Remy Lebeautearfur 更新时间:11/16/2023 访问量:96

问:

我有一种情况,我需要用它来重命名一个文件夹,该文件夹包含一些具有打开文件句柄的文件(在同一过程中)。MoveFileA()

如果一切都在同一线程中完成,则有效,但是,如果这些文件句柄是在另一个线程中打开的,则重命名操作将不起作用。

据我所知,Windows 文件句柄应该在线程之间共享。那么,为什么这不起作用呢?

我有一个小的测试程序来演示我的问题。如果我按原样编译并运行它,它会完成而不会出错。但是,如果我注释掉这一行:

auto handles = create3Handles();

并取消注释此行:

auto handles = create3HandlesInThread();

然后失败,程序打印出来:MoveFileA()

移动文件夹失败 (5)

#include <array>
#include <cstddef>
#include <filesystem>
#include <iostream>
#include <string>
#include <thread>

#include <windows.h>

using namespace std::literals;

static auto constexpr ParentDir = "test-dir";
static auto constexpr ParentDirAfter = "renamed-dir";

std::array<HANDLE, 3U> create3Handles() {
    auto handles = std::array<HANDLE, 3U>{};
    for (size_t i = 0U; i < std::size(handles); ++i) {
        auto filepath = std::string{ParentDir} + '/' + std::to_string(i) + ".txt";
        if (handles[i] = CreateFileA(filepath.c_str(),
                    GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    nullptr,
                    CREATE_ALWAYS,
                    FILE_ATTRIBUTE_NORMAL,
                    nullptr); handles[i] == INVALID_HANDLE_VALUE) {
            std::cerr << "Create \"" << filepath << "\" failed (" << GetLastError() << ')' << std::endl;
        }

        if (BOOL ret = WriteFile(handles[i],
                    filepath.c_str(),
                    std::size(filepath) * sizeof(filepath[0]),
                    nullptr,
                    nullptr); ret == 0) {
            std::cerr << "Write \"" << filepath << "\" failed (" << GetLastError() << ')' << std::endl;
        }
    }

    return handles;
}

[[maybe_unused]] std::array<HANDLE, 3U> create3HandlesInThread() {
    auto handles = std::array<HANDLE, 3>{};

    auto t = std::thread{[&handles]() { handles = create3Handles(); }};
    t.join();

    return handles;
}

int main() {
    std::filesystem::remove_all({ ParentDir });
    std::filesystem::remove_all({ ParentDirAfter });

    if (BOOL ret = CreateDirectoryA(ParentDir, nullptr); !ret) {
        std::cerr << "Create parent folder failed" << GetLastError() << ')' << std::endl;
        return 1;
    }

    auto handles = create3Handles();
//    auto handles = create3HandlesInThread();

    if (BOOL ret = MoveFileA(ParentDir, ParentDirAfter); !ret) {
        std::cerr << "Move folder failed (" << GetLastError() << ')' << std::endl;
    }

    for (auto const &handle: handles) {
        if (BOOL ret = CloseHandle(handle); !ret) {
            std::cerr << "Close file handle failed (" << GetLastError() << ')' << std::endl;
        }
    }

    return 0;
}
C++ Windows WinAPI

评论

5赞 Paul Sanders 11/15/2023
谁打开了文件并不重要。如果它们是开放的,它们就是开放的,你就会被灌水。
0赞 Jeremy Friesner 11/15/2023
@PaulSanders这也是我所期望的,但如果是这样的话,为什么当句柄是在同一线程中创建时,程序似乎适用于 OP?
1赞 Paul Sanders 11/16/2023
@JeremyFriesner我无法解释这一点,但我会邀请OP仔细检查他的发现。
0赞 Jeaninez - MSFT 11/16/2023
根据 Doc:MoveFileA 函数:MoveFile 函数将在同一目录中或跨目录移动(重命名)文件或目录(包括其子目录)。需要注意的是,当目标位于不同的卷上时,MoveFile 函数将在目录移动时失败。
1赞 Paul Sanders 11/18/2023
Unix 对打开的文件执行操作的容忍度要高得多。

答:

0赞 tearfur 11/16/2023 #1

事实证明,在这两种情况下都不应该重命名文件夹,我的测试程序证明了这一点。MoveFileA()

我不知道我或我的电脑发生了什么,因为我可以发誓正在工作。很抱歉浪费了大家的时间。create3Handles()