我尝试使用SetFileInformationByHandle
移动文件。Niall道格拉斯在他的CppCon 2015演讲“Racing The File System”中提出了这种技术,作为原子移动/重命名文件的一种方法。然而,我很难提供正确的参数;它总是失败,GetLastError
返回ERROR_INVALID_PARAMETER
。
我已经尝试了以下设置,使用Unicode字符集:
- VS2015 U1,在Windows 10下运行exe
- VS2015 U2,在Windows Server 2012下运行exe
- VS 2013,在Windows 7下运行exe
但是行为是一样的。我确保可以访问测试文件夹和测试文件。
#include <sdkddkver.h>
#include <windows.h>
#include <cstring>
#include <iostream>
#include <memory>
int main()
{
auto const& filepath = L"C:\\remove_tests\\file.txt";
auto const& destpath = L"C:\\remove_tests\\other.txt";
// unclear if that's the "root directory"
auto const& rootdir = L"C:\\remove_tests";
// handles will be leaked but that should be irrelevant here
auto const f_handle = CreateFile(filepath,
GENERIC_READ | GENERIC_WRITE | DELETE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (f_handle == INVALID_HANDLE_VALUE)
{
auto const err = GetLastError();
std::cerr << "failed to create test file: " << err;
return err;
}
auto const parent_dir_handle = CreateFile(rootdir,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (parent_dir_handle == INVALID_HANDLE_VALUE)
{
auto const err = GetLastError();
std::cerr << "failed to get handle to parent directory: " << err;
return err;
}
auto const destpath_bytes_with_null = sizeof(destpath);
// unclear if we need to subtract the one wchar_t of FileNameLength:
auto const struct_size = sizeof(FILE_RENAME_INFO) + destpath_bytes_with_null;
auto const buf = std::make_unique<char[]>(struct_size);
auto const fri = reinterpret_cast<FILE_RENAME_INFO*>(buf.get());
fri->ReplaceIfExists = TRUE; // as described by Niall Douglas
fri->RootDirectory = parent_dir_handle;
// with or without null terminator?
fri->FileNameLength = destpath_bytes_with_null;
std::memcpy(fri->FileName, destpath, destpath_bytes_with_null);
BOOL res = SetFileInformationByHandle(f_handle, FileRenameInfo,
fri, struct_size);
if (!res)
{
auto const err = GetLastError();
std::cerr << "failed to rename file: " << err;
return err;
}
else
std::cout << "success";
}
字符串
我特别提出的问题是:
FILE_RENAME_INFO
要求的“根目录”是什么?- 句柄需要哪些权限?
SetFileInformationByHandle
产生的ERROR_INVALID_PARAMETER
的潜在问题是什么?
3条答案
按热度按时间db2dz4w81#
我改变了几个想法:
1)我不使用根句柄(我将其设置为NULL)
2)我更改了您的FILE_RENAME_INFO内存分配代码
注意:在Windows 8中检查,在同一卷(磁盘)中移动文件
字符串
fykwrbwg2#
包含
FileRenameInfo
和FILE_RENAME_INFO
的SetFileInformationByHandle
的文档包含一些错误。FILE_RENAME_INFO.FileNameLength
必须设置为复制到FILE_RENAME_INFO.FileName
的字符数(不包括终止零),FILE_RENAME_INFO.RootDirectory
必须为null,即使将文件从一个目录移动到另一个目录。字符串
xytpbqjk3#
有点偏离了主要问题,但是为什么不尝试C++17
filesystem
库作为标准的可移植方式呢?你可以简单地使用**std::filesystem::rename
,MSVC也支持。我没有提供一个例子,因为它本质上是上面第一个链接中cppreference**例子的副本。它有一个更简单直接的API。