SHFileOperation 使目录 "undeletable"
SHFileOperation makes directory "undeletable"
好的,所以我有一个应用程序在 %TEMP% 目录中创建一个子目录来保存一些中间文件,而应用程序仍在运行。而且,当应用程序即将退出时,我会调用一个清理函数,以便递归再次从 %TEMP% 中删除我的子目录,所以我们不会留下任何"garbage" 个文件落后。到目前为止,这在 99.9% 的所有情况下都能正常工作。但在某些非常罕见的情况下,清理功能无法删除我的子目录!测试表明,当清理成功时,它总是在 第一次 尝试中这样做。而且,当第一次尝试失败时,重试也无济于事。我可以重试 9999 次,它会再次失败 9999 次。糟透了!
所以,很明显,"my" 目录由于某种原因被 封锁。现在,如果 "my" 目录中仍然有一些文件(或子目录),我就会明白这一点。但它完全是空的! 没有 个文件(甚至没有隐藏文件!)和没有 个子目录。只是一个空目录。它仍然可以不被删除。让事情变得更加晦涩的是,它只是未能从我自己的进程中删除。它 可以 从 Windows Explorer 或 Total Commander 中完全删除 - 即使我的进程仍在运行。
现在,经过几个小时的测试,我发现 SHFileOperation() 是罪魁祸首。在某些情况下,我使用 SHFileOperation() 到 copy 一个文件 到 "my" 目录.而且,每当我这样做时,都会出于未公开的原因创建该目录 "undeletable"!再次说明一下:我通过SHFileOperation()复制到"my"目录的file完全可以删除。它是可以 no 不再被删除的目录 - 只能从 "my" 进程中删除。使用 CopyFile() 而不是 SHFileOperation() 立即解决了问题。我仍然想了解 SHFileOperation() 函数发生了什么...
如果有人有想法,我很乐意知道...
此致!
~更新#1~
好的,所以我按照要求整理了一个例子:
#define COPY_METHOD 0
static wchar_t *pathToBuffer(const QString &path)
{
const QString nativePath = QDir::toNativeSeparators(path);
wchar_t *buffer = new wchar_t[nativePath.length() + 2];
wcscpy_s(buffer, path.length() + 2, (const wchar_t*) nativePath.utf16());
buffer[nativePath.length()] = buffer[nativePath.length() + 1] = L'[=11=]';
return buffer;
}
bool some_function(const QString &sourceFile, const QString &outputFile, volatile bool *abortFlag)
{
emit messageLogged(QString("Copy file \"%1\" to \"%2\"").arg(sourceFile, outputFile));
/*---------------------------*/
#if(COPY_METHOD == 0)
/*---------------------------*/
QScopedArrayPointer<wchar_t> srcBuffer(pathToBuffer(sourceFile));
QScopedArrayPointer<wchar_t> outBuffer(pathToBuffer(outputFile));
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = srcBuffer.data();
fileOperation.pTo = outBuffer.data();
emit statusUpdated(0);
int result = SHFileOperationW(&fileOperation);
emit statusUpdated(100);
return (result == 0 && fileOperation.fAnyOperationsAborted == false);
/*---------------------------*/
#elif(COPY_METHOD == 1)
/*---------------------------*/
emit statusUpdated(0);
const BOOL success = CopyFile((const wchar_t*) QDir::toNativeSeparators(sourceFile).utf16(), (const wchar_t*) QDir::toNativeSeparators(outputFile).utf16(), FALSE);
emit statusUpdated(100);
return (success != FALSE);
/*---------------------------*/
#elif(COPY_METHOD == 2)
/*---------------------------*/
if(QFile::exists(outputFile))
{
QFile::remove(outputFile);
}
emit statusUpdated(0);
QFile src(sourceFile);
const bool success = src.copy(outputFile);
emit statusUpdated(100);
if(!success)
{
emit messageLogged(QString("Failed to copy file: %1").arg(src.errorString()));
}
return success;
/*---------------------------*/
#else
/*---------------------------*/
#error Invalid copy method specified!
/*---------------------------*/
#endif //COPY_METHOD
/*---------------------------*/
}
方法#0是我原来做的。而且,一旦我这样做,文件被复制到的目录(不是文件本身!)稍后会变成 "undeletable"。这是可重现的。而且,只要我使用方法 #1 或方法 #2 (以及 no 其他更改都做了)问题解决了...
在第一个 post 中,我说过这种情况很少发生。是的,因为(通常)函数 "some_function" 很少被调用。但是当这个函数被调用时(或者当我 force 它被调用时),问题就会发生 always - 至少只要我还在使用复制方法#0.
最后,当目录不能被删除时,系统返回的错误是一个很不明确的"access denied"(Win32错误0x5),所以这不是'帮助不大。如果确实发生,那么 RemoveDirectoy() 函数以及 QDir::rmdir() 方法都会出现问题。目录为空.
~更新#2~
好的,这是一个完整的示例,改编自 Harry Johnston 提供的代码:
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP", NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=12=]";
const wchar_t *const outputFile = L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP\test.wav[=12=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
if(!DeleteFileW(outputFile))
{
printf("DeleteFile Failed: Error %u\n", GetLastError());
return 1;
}
if (!RemoveDirectoryW(L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"))
{
printf("RemoveDirectory Failed: Error %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
结果是: RemoveDirectory 失败:错误 5
您正在复制的文件具有本地化的 shell 文件名,查看源文件夹中的 desktop.ini
文件可以看出:
c:\windows\media\desktop.ini
shell复制操作复制这个本地化名称,意思是:
在目标文件夹中创建一个 desktop.ini
文件
将目标文件夹标记为只读
要删除目录,您需要删除desktop.ini
并重置只读标志。
您可以手动执行此操作:
#include <Windows.h>
#include <stdio.h>
#define target L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(target, NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=11=]";
const wchar_t *const outputFile = target L"\test.wav[=11=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
if(!DeleteFileW(outputFile))
{
printf("DeleteFile(outputFile) Failed: Error %u\n", GetLastError());
return 1;
}
if (!DeleteFileW(target L"\desktop.ini"))
{
printf("DeleteFile(desktop.ini) Failed: Error %u\n", GetLastError());
return 1;
}
if (!SetFileAttributes(target, 0))
{
printf("SetFileAttributes Failed: Error %u\n", GetLastError());
return 1;
}
if (!RemoveDirectoryW(target))
{
printf("RemoveDirectory Failed: Error %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
但是因为你用shell创建了目标目录,所以用同样的方法删除它会更一致,当然这会为你处理细节:
#include <Windows.h>
#include <stdio.h>
#define target L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(target, NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=12=]";
const wchar_t *const outputFile = target L"\test.wav[=12=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_DELETE;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
fileOperation.pFrom = target L"[=12=]";
result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
printf("OK\n");
return 0;
}
好的,所以我有一个应用程序在 %TEMP% 目录中创建一个子目录来保存一些中间文件,而应用程序仍在运行。而且,当应用程序即将退出时,我会调用一个清理函数,以便递归再次从 %TEMP% 中删除我的子目录,所以我们不会留下任何"garbage" 个文件落后。到目前为止,这在 99.9% 的所有情况下都能正常工作。但在某些非常罕见的情况下,清理功能无法删除我的子目录!测试表明,当清理成功时,它总是在 第一次 尝试中这样做。而且,当第一次尝试失败时,重试也无济于事。我可以重试 9999 次,它会再次失败 9999 次。糟透了!
所以,很明显,"my" 目录由于某种原因被 封锁。现在,如果 "my" 目录中仍然有一些文件(或子目录),我就会明白这一点。但它完全是空的! 没有 个文件(甚至没有隐藏文件!)和没有 个子目录。只是一个空目录。它仍然可以不被删除。让事情变得更加晦涩的是,它只是未能从我自己的进程中删除。它 可以 从 Windows Explorer 或 Total Commander 中完全删除 - 即使我的进程仍在运行。
现在,经过几个小时的测试,我发现 SHFileOperation() 是罪魁祸首。在某些情况下,我使用 SHFileOperation() 到 copy 一个文件 到 "my" 目录.而且,每当我这样做时,都会出于未公开的原因创建该目录 "undeletable"!再次说明一下:我通过SHFileOperation()复制到"my"目录的file完全可以删除。它是可以 no 不再被删除的目录 - 只能从 "my" 进程中删除。使用 CopyFile() 而不是 SHFileOperation() 立即解决了问题。我仍然想了解 SHFileOperation() 函数发生了什么...
如果有人有想法,我很乐意知道...
此致!
~更新#1~
好的,所以我按照要求整理了一个例子:
#define COPY_METHOD 0
static wchar_t *pathToBuffer(const QString &path)
{
const QString nativePath = QDir::toNativeSeparators(path);
wchar_t *buffer = new wchar_t[nativePath.length() + 2];
wcscpy_s(buffer, path.length() + 2, (const wchar_t*) nativePath.utf16());
buffer[nativePath.length()] = buffer[nativePath.length() + 1] = L'[=11=]';
return buffer;
}
bool some_function(const QString &sourceFile, const QString &outputFile, volatile bool *abortFlag)
{
emit messageLogged(QString("Copy file \"%1\" to \"%2\"").arg(sourceFile, outputFile));
/*---------------------------*/
#if(COPY_METHOD == 0)
/*---------------------------*/
QScopedArrayPointer<wchar_t> srcBuffer(pathToBuffer(sourceFile));
QScopedArrayPointer<wchar_t> outBuffer(pathToBuffer(outputFile));
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = srcBuffer.data();
fileOperation.pTo = outBuffer.data();
emit statusUpdated(0);
int result = SHFileOperationW(&fileOperation);
emit statusUpdated(100);
return (result == 0 && fileOperation.fAnyOperationsAborted == false);
/*---------------------------*/
#elif(COPY_METHOD == 1)
/*---------------------------*/
emit statusUpdated(0);
const BOOL success = CopyFile((const wchar_t*) QDir::toNativeSeparators(sourceFile).utf16(), (const wchar_t*) QDir::toNativeSeparators(outputFile).utf16(), FALSE);
emit statusUpdated(100);
return (success != FALSE);
/*---------------------------*/
#elif(COPY_METHOD == 2)
/*---------------------------*/
if(QFile::exists(outputFile))
{
QFile::remove(outputFile);
}
emit statusUpdated(0);
QFile src(sourceFile);
const bool success = src.copy(outputFile);
emit statusUpdated(100);
if(!success)
{
emit messageLogged(QString("Failed to copy file: %1").arg(src.errorString()));
}
return success;
/*---------------------------*/
#else
/*---------------------------*/
#error Invalid copy method specified!
/*---------------------------*/
#endif //COPY_METHOD
/*---------------------------*/
}
方法#0是我原来做的。而且,一旦我这样做,文件被复制到的目录(不是文件本身!)稍后会变成 "undeletable"。这是可重现的。而且,只要我使用方法 #1 或方法 #2 (以及 no 其他更改都做了)问题解决了...
在第一个 post 中,我说过这种情况很少发生。是的,因为(通常)函数 "some_function" 很少被调用。但是当这个函数被调用时(或者当我 force 它被调用时),问题就会发生 always - 至少只要我还在使用复制方法#0.
最后,当目录不能被删除时,系统返回的错误是一个很不明确的"access denied"(Win32错误0x5),所以这不是'帮助不大。如果确实发生,那么 RemoveDirectoy() 函数以及 QDir::rmdir() 方法都会出现问题。目录为空.
~更新#2~
好的,这是一个完整的示例,改编自 Harry Johnston 提供的代码:
#include <Windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP", NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=12=]";
const wchar_t *const outputFile = L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP\test.wav[=12=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
if(!DeleteFileW(outputFile))
{
printf("DeleteFile Failed: Error %u\n", GetLastError());
return 1;
}
if (!RemoveDirectoryW(L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"))
{
printf("RemoveDirectory Failed: Error %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
结果是: RemoveDirectory 失败:错误 5
您正在复制的文件具有本地化的 shell 文件名,查看源文件夹中的 desktop.ini
文件可以看出:
c:\windows\media\desktop.ini
shell复制操作复制这个本地化名称,意思是:
在目标文件夹中创建一个
desktop.ini
文件将目标文件夹标记为只读
要删除目录,您需要删除desktop.ini
并重置只读标志。
您可以手动执行此操作:
#include <Windows.h>
#include <stdio.h>
#define target L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(target, NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=11=]";
const wchar_t *const outputFile = target L"\test.wav[=11=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
if(!DeleteFileW(outputFile))
{
printf("DeleteFile(outputFile) Failed: Error %u\n", GetLastError());
return 1;
}
if (!DeleteFileW(target L"\desktop.ini"))
{
printf("DeleteFile(desktop.ini) Failed: Error %u\n", GetLastError());
return 1;
}
if (!SetFileAttributes(target, 0))
{
printf("SetFileAttributes Failed: Error %u\n", GetLastError());
return 1;
}
if (!RemoveDirectoryW(target))
{
printf("RemoveDirectory Failed: Error %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
但是因为你用shell创建了目标目录,所以用同样的方法删除它会更一致,当然这会为你处理细节:
#include <Windows.h>
#include <stdio.h>
#define target L"C:\Users\MuldeR\AppData\Local\Temp\umTpRwui9MpP"
int main(int argc, char ** argv)
{
if(!CreateDirectoryW(target, NULL))
{
printf("CreateDirectory Failed: Error %u\n", GetLastError());
return 1;
}
const wchar_t *const sourceFile = L"C:\Windows\Media\ding.wav[=12=]";
const wchar_t *const outputFile = target L"\test.wav[=12=]";
SHFILEOPSTRUCTW fileOperation;
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_COPY;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_FILESONLY;
fileOperation.pFrom = sourceFile;
fileOperation.pTo = outputFile;
int result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
memset(&fileOperation, 0, sizeof(SHFILEOPSTRUCTW));
fileOperation.wFunc = FO_DELETE;
fileOperation.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI;
fileOperation.pFrom = target L"[=12=]";
result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
printf("OK\n");
return 0;
}