SetFileInformationByHandle
和FILE_DISPOSITION_INFO
参数的函数可以设置一个打开句柄的文件在所有句柄关闭时被删除。然而,我正在尝试根据由FILE_DISPOSITION_INFO
和OpenFileById
检索到的文件索引(磁盘 ID)来删除文件,以便安全地删除仅大小写不同的目录中的文件/目录。在我的情况下,这样做是安全的,因为在 NTFS 系统上,文件索引在删除之前是 持久的,使得当前代码库处理的ReplaceFile
不再需要。但是,尝试删除该句柄时,我会收到错误 87 (ERROR_INVALID_PARAMETER
)。如果使用使用CreateFileW
创建的句柄进行删除,则不会遇到任何问题。然而,我不能这样做,因为 Windows 将无法区分大小写相同的两个文件/文件夹(尽管 NTFS 可以)。我还知道使用OpenFileById
打开的硬链接文件存在歧义,因为硬链接文件共享相同的磁盘 ID。对于此场景,硬链接文件的问题可以忽略不计。我只会根据 ID 删除目录,这些目录不能与硬链接。有一个参数或设置我在OpenFileById
调用或SetFileInformationByHandle
调用中遗漏的吗?我尝试过的其他方法:
- 使用
DuplicateHandle
与OpenFileById
句柄,提供DELETE
给dwDesiredAccess
并使用它。 结果仍然是ERROR_INVALID_PARAMETER
。 - 使用
ReOpenFile
与OpenFileById
句柄,提供DELETE
给dwDesiredAccess
并使用它。 结果仍然是ERROR_INVALID_PARAMETER
。 - 使用
ReOpenFile
与OpenFileById
句柄,提供DELETE
给dwDesiredAccess
并提供FILE_FLAG_DELETE_ON_CLOSE
标志。 没有错误提示,但文件在所有句柄关闭后仍然存在。
下面是一个最小化但完整的示例代码,可重现该问题:
#include <stdio.h>
#include <sys/stat.h>
#include <Windows.h>
DWORD getFileID(LPCWSTR path, LARGE_INTEGER *id)
{
HANDLE h = CreateFileW(path, 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (h == INVALID_HANDLE_VALUE)
return GetLastError();
BY_HANDLE_FILE_INFORMATION info;
if (!GetFileInformationByHandle(h, &info))
{
DWORD err = GetLastError();
CloseHandle(h);
return err;
}
id->HighPart = info.nFileIndexHigh;
id->LowPart = info.nFileIndexLow;
CloseHandle(h);
return ERROR_SUCCESS;
}
DWORD deleteFileHandle(HANDLE fileHandle)
{
FILE_DISPOSITION_INFO info;
info.DeleteFileW = TRUE;
if (!SetFileInformationByHandle(
fileHandle, FileDispositionInfo, &info, sizeof(info)))
{
return GetLastError();
}
return ERROR_SUCCESS;
}
int wmain(DWORD argc, LPWSTR argv[])
{
if (argc != 3)
{
fwprintf(stderr, L"Arguments: <rootpath> <path>\n");
return 1;
}
DWORD err;
HANDLE rootHandle = CreateFileW(
argv[1], 0, 0, 0, OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT |
FILE_FLAG_BACKUP_SEMANTICS |
FILE_FLAG_POSIX_SEMANTICS,
0);
if (rootHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
fwprintf(stderr,
L"Could not open root directory '%s', error code %d\n",
argv[1], err);
return err;
}
LARGE_INTEGER fileID;
err = getFileID(argv[2], &fileID);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not get file ID of file/directory '%s', error code %d\n",
argv[2], err);
CloseHandle(rootHandle);
return err;
}
fwprintf(stdout,
L"The file ID of '%s' is %lld\n",
argv[2], fileID.QuadPart);
FILE_ID_DESCRIPTOR idStruct;
idStruct.Type = FileIdType;
idStruct.FileId = fileID;
HANDLE fileHandle = OpenFileById(
rootHandle, &idStruct, DELETE, FILE_SHARE_DELETE, 0,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
if (fileHandle == INVALID_HANDLE_VALUE)
{
err = GetLastError();
CloseHandle(rootHandle);
fwprintf(stderr,
L"Could not open file by ID %lld, error code %d\n",
fileID.QuadPart, err);
return err;
}
err = deleteFileHandle(fileHandle);
if (err != ERROR_SUCCESS)
{
fwprintf(stderr,
L"Could not delete file by ID '%lld', error code %d\n",
fileID.QuadPart, err);
}
CloseHandle(fileHandle);
struct _stat _tmp;
fwprintf(stdout,
L"File was %ssuccessfully deleted\n",
(_wstat(argv[2], &_tmp) == 0) ? L"not " : L"");
CloseHandle(rootHandle);
return err;
}
任何解决方案必须与Vista及以上版本兼容。欢迎提出代码改进建议。
SetFileInformationByHandle
返回ERROR_INVALID_HANDLE
。还可以找到OpenFileById
句柄的第一个文件路径。最后,对于包含“无效”字符的路径,如\
或*
,重命名根本不起作用。 - Alyssa Haroldsen