summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qfilesystemengine_win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/io/qfilesystemengine_win.cpp')
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index 0579872f8d..36d43e9cb7 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -41,6 +41,7 @@
#include "qoperatingsystemversion.h"
#include "qplatformdefs.h"
#include "qsysinfo.h"
+#include "qscopeguard.h"
#include "private/qabstractfileengine_p.h"
#include "private/qfsfileengine_p.h"
#include <private/qsystemlibrary_p.h>
@@ -59,6 +60,8 @@
#include <objbase.h>
#ifndef Q_OS_WINRT
# include <shlobj.h>
+# include <shobjidl.h>
+# include <shellapi.h>
# include <lm.h>
# include <accctrl.h>
#endif
@@ -422,6 +425,104 @@ static inline bool getFindData(QString path, WIN32_FIND_DATA &findData)
return false;
}
+#if defined(__IFileOperation_INTERFACE_DEFINED__)
+class FileOperationProgressSink : public IFileOperationProgressSink
+{
+public:
+ FileOperationProgressSink()
+ : ref(1)
+ {}
+ virtual ~FileOperationProgressSink() {}
+
+ ULONG STDMETHODCALLTYPE AddRef()
+ {
+ return ++ref;
+ }
+ ULONG STDMETHODCALLTYPE Release()
+ {
+ if (--ref == 0) {
+ delete this;
+ return 0;
+ }
+ return ref;
+ }
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
+ {
+ if (!ppvObject)
+ return E_POINTER;
+
+ *ppvObject = nullptr;
+
+ if (iid == __uuidof(IUnknown)) {
+ *ppvObject = static_cast<IUnknown*>(this);
+ } else if (iid == __uuidof(IFileOperationProgressSink)) {
+ *ppvObject = static_cast<IFileOperationProgressSink*>(this);
+ }
+
+ if (*ppvObject) {
+ AddRef();
+ return S_OK;
+ }
+
+ return E_NOINTERFACE;
+ }
+
+ HRESULT STDMETHODCALLTYPE StartOperations()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreRenameItem(DWORD, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostRenameItem(DWORD, IShellItem *, LPCWSTR, HRESULT, IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostMoveItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR )
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostCopyItem(DWORD, IShellItem *, IShellItem *, LPCWSTR, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, IShellItem *)
+ {
+ // stop the operation if the file will be deleted rather than trashed
+ return (dwFlags & TSF_DELETE_RECYCLE_IF_POSSIBLE) ? S_OK : E_FAIL;
+ }
+ HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD /* dwFlags */, IShellItem * /* psiItem */,
+ HRESULT /* hrDelete */, IShellItem *psiNewlyCreated)
+ {
+ if (psiNewlyCreated) {
+ wchar_t *pszName = nullptr;
+ psiNewlyCreated->GetDisplayName(SIGDN_FILESYSPATH, &pszName);
+ if (pszName) {
+ targetPath = QString::fromWCharArray(pszName);
+ CoTaskMemFree(pszName);
+ }
+ }
+ return S_OK;
+ }
+ HRESULT STDMETHODCALLTYPE PreNewItem(DWORD, IShellItem *, LPCWSTR)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PostNewItem(DWORD, IShellItem *, LPCWSTR, LPCWSTR, DWORD, HRESULT,
+ IShellItem *)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE UpdateProgress(UINT,UINT)
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE ResetTimer()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE PauseTimer()
+ { return S_OK; }
+ HRESULT STDMETHODCALLTYPE ResumeTimer()
+ { return S_OK; }
+
+ QString targetPath;
+private:
+ ULONG ref;
+};
+#endif
+
bool QFileSystemEngine::uncListSharesOnServer(const QString &server, QStringList *list)
{
DWORD res = ERROR_NOT_SUPPORTED;
@@ -1431,6 +1532,103 @@ bool QFileSystemEngine::removeFile(const QFileSystemEntry &entry, QSystemError &
return ret;
}
+/*
+ If possible, we use the IFileOperation implementation, which allows us to determine
+ the location of the object in the trash.
+ If not (likely on mingw), we fall back to the old API, which won't allow us to know
+ that.
+*/
+//static
+bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
+ QFileSystemEntry &newLocation, QSystemError &error)
+{
+#ifndef Q_OS_WINRT
+ // we need the "display name" of the file, so can't use nativeFilePath
+ const QString sourcePath = QDir::toNativeSeparators(source.filePath());
+
+ /*
+ Windows 7 insists on showing confirmation dialogs and ignores the respective
+ flags set on IFileOperation. Fall back to SHFileOperation, even if it doesn't
+ give us the new location of the file.
+ */
+ if (QOperatingSystemVersion::current() > QOperatingSystemVersion::Windows7) {
+# if defined(__IFileOperation_INTERFACE_DEFINED__)
+ CoInitialize(NULL);
+ IFileOperation *pfo = nullptr;
+ IShellItem *deleteItem = nullptr;
+ FileOperationProgressSink *sink = nullptr;
+ HRESULT hres = E_FAIL;
+
+ auto coUninitialize = qScopeGuard([&](){
+ if (sink)
+ sink->Release();
+ if (deleteItem)
+ deleteItem->Release();
+ if (pfo)
+ pfo->Release();
+ CoUninitialize();
+ if (!SUCCEEDED(hres))
+ error = QSystemError(hres, QSystemError::NativeError);
+ });
+
+ hres = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pfo));
+ if (!pfo)
+ return false;
+ pfo->SetOperationFlags(FOF_ALLOWUNDO | FOFX_RECYCLEONDELETE | FOF_NOCONFIRMATION
+ | FOF_SILENT | FOF_NOERRORUI);
+ hres = SHCreateItemFromParsingName(reinterpret_cast<const wchar_t*>(sourcePath.utf16()),
+ nullptr, IID_PPV_ARGS(&deleteItem));
+ if (!deleteItem)
+ return false;
+ sink = new FileOperationProgressSink;
+ hres = pfo->DeleteItem(deleteItem, static_cast<IFileOperationProgressSink*>(sink));
+ if (!SUCCEEDED(hres))
+ return false;
+ hres = pfo->PerformOperations();
+ if (!SUCCEEDED(hres))
+ return false;
+ newLocation = QFileSystemEntry(sink->targetPath);
+
+# endif // no IFileOperation in SDK (mingw, likely) - fall back to SHFileOperation
+ } else {
+ // double null termination needed, so can't use QString::utf16
+ QVarLengthArray<wchar_t, MAX_PATH + 1> winFile(sourcePath.length() + 2);
+ sourcePath.toWCharArray(winFile.data());
+ winFile[sourcePath.length()] = wchar_t{};
+ winFile[sourcePath.length() + 1] = wchar_t{};
+
+ SHFILEOPSTRUCTW operation;
+ operation.hwnd = nullptr;
+ operation.wFunc = FO_DELETE;
+ operation.pFrom = winFile.constData();
+ operation.pTo = nullptr;
+ operation.fFlags = FOF_ALLOWUNDO | FOF_NO_UI;
+ operation.fAnyOperationsAborted = FALSE;
+ operation.hNameMappings = nullptr;
+ operation.lpszProgressTitle = nullptr;
+
+ int result = SHFileOperation(&operation);
+ if (result != 0) {
+ error = QSystemError(result, QSystemError::NativeError);
+ return false;
+ }
+ /*
+ This implementation doesn't let us know where the file ended up, even if
+ we would specify FOF_WANTMAPPINGHANDLE | FOF_RENAMEONCOLLISION, as
+ FOF_RENAMEONCOLLISION has no effect unless files are moved, copied, or renamed.
+ */
+ Q_UNUSED(newLocation);
+ }
+ return true;
+
+#else // Q_OS_WINRT
+ Q_UNUSED(source);
+ Q_UNUSED(newLocation);
+ Q_UNUSED(error);
+ return false;
+#endif
+}
+
//static
bool QFileSystemEngine::setPermissions(const QFileSystemEntry &entry, QFile::Permissions permissions, QSystemError &error,
QFileSystemMetaData *data)