Reputation: 577
Okay, so I have an application that creates a sub-directory inside the %TEMP% directory to keep some intermediate files, while the application is still running. And, when the application is about to exit, I will call a clean-up function in order to recursively delete my sub-directory from %TEMP% again, so we don't leave any "garbage" files behind. So far, this works fine in 99.9% of all cases. But in some very rare cases, the clean-up function failed to delete my sub-directory! Testing showed that, when the clean-up succeeds, it always does so in the first attempt. And, when it failed, in the first attempt, retrying didn't help. I can retry 9999 times and it would fail 9999 times again. Bummer!
So, obviously, "my" directory was blocked for some reason. Now, I would have understood this, if there still had been some file (or sub-directory) within "my" directory. But it was completely empty! No files (not even hidden ones!) and no sub-directories. Just an empty directory. Still it could not be deleted. To make things even more obscure, it only failed to be deleted from my own process. It could be deleted from Windows Explorer or Total Commander perfectly fine - even while my process was still running.
Now, after many hours of testing, I figured out that SHFileOperation() is to blame. In some cases, I used SHFileOperation() to copy a file to "my" directory. And, whenever I did that, this made that directory "undeletable" for an undisclosed reason! To make it clear again: The file that I copied to "my" directory via SHFileOperation() could be deleted perfectly fine. It's the directory that could no longer be deleted - from within "my" process only. Using CopyFile() instead of SHFileOperation() immediately resolved the issue. Still I would like to understand what's going on with the SHFileOperation() function...
If anybody has an idea, I would be happy to know...
Regards!
~ UPDATE #1 ~
Okay, so I have put together an example, as requested:
#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'\0';
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
/*---------------------------*/
}
Method #0 is what I was doing originally. And, as soon as I do that, the directory where the file has been copied to (not the file itself!) becomes "undeletable" later on. This is reproducible. And, as soon as I use either method #1 or method #2 (and no other changes are made at all) the problem is resolved...
In the first post I said that it only happens rarely. Yes, because (normally) the function "some_function" is only called rarely. But when this function gets called (or when I force it to be called), then the problem happens always - at least as long as I still use copy method #0.
Finally, when the directory can not be deleted, the error returned by the system is a very unspecific "access denied" (Win32 Error 0x5), so this doesn't help much. If it does occurs, then the problem occurs with the RemoveDirectoy() function as well as the QDir::rmdir() method. The directory is empty.
~ UPDATE #2 ~
Okay, here is a full example, adapted from code provided by 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\0";
const wchar_t *const outputFile = L"C:\\Users\\MuldeR\\AppData\\Local\\Temp\\umTpRwui9MpP\\test.wav\0";
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;
}
Result is: RemoveDirectory Failed: Error 5
Upvotes: 1
Views: 1067
Reputation: 36318
The file you're copying has a localized shell filename, as can be seen by looking in the desktop.ini
file in the source folder:
c:\windows\media\desktop.ini
The shell copy operation copies this localized name, which means it:
creates a desktop.ini
file in the target folder
marks the target folder read-only
To remove the directory, you need to delete desktop.ini
and reset the read-only flag.
You could do this manually:
#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\0";
const wchar_t *const outputFile = target L"\\test.wav\0";
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;
}
But since you created the target directory with the shell, it would be more consistent to delete it the same way, and of course that takes care of the details for you:
#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\0";
const wchar_t *const outputFile = target L"\\test.wav\0";
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"\0";
result = SHFileOperationW(&fileOperation);
if (result != 0)
{
printf("SHFileOperation Failed: Error%u\n", result);
return 1;
}
printf("OK\n");
return 0;
}
Upvotes: 1