Author: akhaldi
Date: Sun Jan 5 12:48:42 2014
New Revision: 61537
URL:
http://svn.reactos.org/svn/reactos?rev=61537&view=rev
Log:
[SHELL32]
* Move shell file operations to background threads to prevent the shell from hanging
during long copies, deletes, and moves.
* Improve drag and drop functionality.
* Add a partial drop handler to the recycle bin.
* Brought to you by Huw Campbell.
CORE-3760
Modified:
trunk/reactos/dll/win32/shell32/defcontextmenu.cpp
trunk/reactos/dll/win32/shell32/folders/fs.cpp
trunk/reactos/dll/win32/shell32/folders/fs.h
trunk/reactos/dll/win32/shell32/folders/recyclebin.cpp
trunk/reactos/dll/win32/shell32/folders/recyclebin.h
trunk/reactos/dll/win32/shell32/shlview.cpp
Modified: trunk/reactos/dll/win32/shell32/defcontextmenu.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/defconte…
==============================================================================
--- trunk/reactos/dll/win32/shell32/defcontextmenu.cpp [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/defcontextmenu.cpp [iso-8859-1] Sun Jan 5 12:48:42
2014
@@ -1037,7 +1037,6 @@
}
SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
- NotifyShellViewWindow(lpcmi, TRUE);
TRACE("CP result %x\n", hr);
return S_OK;
@@ -1104,73 +1103,24 @@
ERR("no IDropTarget Interface\n");
return hr;
}
- //DWORD link = DROPEFFECT_LINK;
SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
- NotifyShellViewWindow(lpcmi, TRUE);
return S_OK;
}
-HRESULT
-CDefaultContextMenu::DoDelete(
- LPCMINVOKECOMMANDINFO lpcmi)
-{
- STRRET strTemp;
- HRESULT hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING,
&strTemp);
- if(hr != S_OK)
- {
- ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
- return hr;
- }
-
- WCHAR wszPath[MAX_PATH];
- hr = StrRetToBufW(&strTemp, m_Dcm.apidl[0], wszPath, _countof(wszPath));
- if (hr != S_OK)
- {
- ERR("StrRetToBufW failed with %x\n", hr);
- return hr;
- }
-
- /* Only keep the base path */
- LPWSTR pwszFilename = PathFindFileNameW(wszPath);
- *pwszFilename = L'\0';
-
- /* Build paths list */
- LPWSTR pwszPaths = BuildPathsList(wszPath, m_Dcm.cidl, m_Dcm.apidl);
- if (!pwszPaths)
+HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi) {
+ TRACE("(%p) Deleting\n", this);
+
+ LPDATAOBJECT pDataObj;
+
+ if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL,
IID_PPV_ARG(IDataObject, &pDataObj))))
+ {
+ pDataObj->AddRef();
+ SHCreateThread(DoDeleteThreadProc, pDataObj, NULL, NULL);
+ pDataObj->Release();
+ }
+ else
return E_FAIL;
-
- /* Delete them */
- SHFILEOPSTRUCTW FileOp;
- ZeroMemory(&FileOp, sizeof(FileOp));
- FileOp.hwnd = GetActiveWindow();
- FileOp.wFunc = FO_DELETE;
- FileOp.pFrom = pwszPaths;
- FileOp.fFlags = FOF_ALLOWUNDO;
-
- if (SHFileOperationW(&FileOp) != 0)
- {
- ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(),
debugstr_w(pwszPaths));
- return S_OK;
- }
-
- /* Get the active IShellView */
- LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd,
CWM_GETISHELLBROWSER, 0, 0);
- if (lpSB)
- {
- /* Is the treeview focused */
- HWND hwnd;
- if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd)))
- {
- /* Remove selected items from treeview */
- HTREEITEM hItem = TreeView_GetSelection(hwnd);
- if (hItem)
- (void)TreeView_DeleteItem(hwnd, hItem);
- }
- }
- NotifyShellViewWindow(lpcmi, TRUE);
-
- HeapFree(GetProcessHeap(), 0, pwszPaths);
return S_OK;
}
Modified: trunk/reactos/dll/win32/shell32/folders/fs.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/folders/…
==============================================================================
--- trunk/reactos/dll/win32/shell32/folders/fs.cpp [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/folders/fs.cpp [iso-8859-1] Sun Jan 5 12:48:42 2014
@@ -1419,6 +1419,27 @@
{
TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
+ _DoDropData *data = reinterpret_cast<_DoDropData*> (HeapAlloc(GetProcessHeap(),
0, sizeof(_DoDropData)));
+ data->This = this;
+ // Need to maintain this class in case the window is closed or the class exists
temporarily (when dropping onto a folder).
+ data->This->AddRef();
+ data->pDataObject = pDataObject;
+ // Also keep the data object in case it gets freed elsewhere.
+ data->pDataObject->AddRef();
+ data->dwKeyState = dwKeyState;
+ data->pt = pt;
+ // Need to dereference as pdweffect is freed.
+ data->pdwEffect = *pdwEffect;
+
+ SHCreateThread(reinterpret_cast<LPTHREAD_START_ROUTINE>
(CFSFolder::_DoDropThreadProc), reinterpret_cast<void *> (data), NULL, NULL);
+ return S_OK;
+}
+
+HRESULT WINAPI CFSFolder::_DoDrop(IDataObject *pDataObject,
+ DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect);
+
HRESULT hr;
bool bCopy = TRUE;
bool bLinking = FALSE;
@@ -1451,9 +1472,9 @@
if (pdwEffect)
{
TRACE("Current drop effect flag %i\n", *pdwEffect);
- if (*pdwEffect & DROPEFFECT_MOVE)
+ if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
bCopy = FALSE;
- if (*pdwEffect & DROPEFFECT_LINK)
+ if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK)
bLinking = TRUE;
}
@@ -1631,5 +1652,12 @@
}
DWORD CFSFolder::_DoDropThreadProc(LPVOID lpParameter) {
+ _DoDropData *data = reinterpret_cast<_DoDropData*>(lpParameter);
+ data->This->_DoDrop(data->pDataObject, data->dwKeyState, data->pt,
&data->pdwEffect);
+ //Release the CFSFolder and data object holds in the copying thread.
+ data->pDataObject->Release();
+ data->This->Release();
+ //Release the parameter from the heap.
+ HeapFree(GetProcessHeap(), 0, data);
return 0;
}
Modified: trunk/reactos/dll/win32/shell32/folders/fs.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/folders/…
==============================================================================
--- trunk/reactos/dll/win32/shell32/folders/fs.h [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/folders/fs.h [iso-8859-1] Sun Jan 5 12:48:42 2014
@@ -46,7 +46,8 @@
BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
void SF_RegisterClipFmt();
BOOL GetUniqueFileName(LPWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget,
BOOL bShortcut);
- DWORD _DoDropThreadProc(LPVOID lpParameter);
+ static DWORD _DoDropThreadProc(LPVOID lpParameter);
+ virtual HRESULT WINAPI _DoDrop(IDataObject *pDataObject, DWORD dwKeyState, POINTL
pt, DWORD *pdwEffect);
public:
CFSFolder();
@@ -116,4 +117,12 @@
END_COM_MAP()
};
+struct _DoDropData {
+ CFSFolder *This;
+ IDataObject *pDataObject;
+ DWORD dwKeyState;
+ POINTL pt;
+ DWORD pdwEffect;
+};
+
#endif // _CFSFOLDER_H_
Modified: trunk/reactos/dll/win32/shell32/folders/recyclebin.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/folders/…
==============================================================================
--- trunk/reactos/dll/win32/shell32/folders/recyclebin.cpp [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/folders/recyclebin.cpp [iso-8859-1] Sun Jan 5
12:48:42 2014
@@ -429,10 +429,24 @@
return S_OK;
}
+/**************************************************************************
+* registers clipboardformat once
+*/
+void CRecycleBin::SF_RegisterClipFmt()
+{
+ TRACE ("(%p)\n", this);
+
+ if (!cfShellIDList)
+ cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
+}
+
CRecycleBin::CRecycleBin()
{
pidl = NULL;
iIdEmpty = 0;
+ cfShellIDList = 0;
+ SF_RegisterClipFmt();
+ fAcceptFmt = FALSE;
}
CRecycleBin::~CRecycleBin()
@@ -556,8 +570,7 @@
if (IsEqualIID (riid, IID_IDropTarget))
{
- WARN ("IDropTarget not implemented\n");
- hr = E_NOTIMPL;
+ hr = this->QueryInterface (IID_IDropTarget, ppv);
}
else if (IsEqualIID (riid, IID_IContextMenu) || IsEqualIID (riid,
IID_IContextMenu2))
{
@@ -605,7 +618,7 @@
{
hr = CRecycleBinItemContextMenuConstructor(riid, apidl[0], (void **)&pObj);
}
- else if (IsEqualIID (riid, IID_IDropTarget) && (cidl >= 1))
+ else if (IsEqualIID (riid, IID_IDropTarget) && (cidl == 1))
{
hr = this->QueryInterface(IID_IDropTarget, (LPVOID *) & pObj);
}
@@ -1372,3 +1385,214 @@
return S_OK;
}
+
+/****************************************************************************
+ * IDropTarget implementation
+ */
+BOOL CRecycleBin::QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
+{
+ /* TODO on shift we should delete, we should update the cursor manager to show this.
*/
+
+ DWORD dwEffect = DROPEFFECT_COPY;
+
+ *pdwEffect = DROPEFFECT_NONE;
+
+ if (fAcceptFmt) { /* Does our interpretation of the keystate ... */
+ *pdwEffect = KeyStateToDropEffect (dwKeyState);
+
+ if (*pdwEffect == DROPEFFECT_NONE)
+ *pdwEffect = dwEffect;
+
+ /* ... matches the desired effect ? */
+ if (dwEffect & *pdwEffect) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+HRESULT WINAPI CRecycleBin::DragEnter(IDataObject *pDataObject,
+ DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ FIXME("Recycle bin drag over (%p)\n", this);
+ /* The recycle bin accepts pretty much everything, and sets a CSIDL flag. */
+ fAcceptFmt = TRUE;
+
+ QueryDrop(dwKeyState, pdwEffect);
+ return S_OK;
+}
+
+HRESULT WINAPI CRecycleBin::DragOver(DWORD dwKeyState, POINTL pt,
+ DWORD *pdwEffect)
+{
+ TRACE("(%p)\n", this);
+
+ if (!pdwEffect)
+ return E_INVALIDARG;
+
+ QueryDrop(dwKeyState, pdwEffect);
+
+ return S_OK;
+}
+
+HRESULT WINAPI CRecycleBin::DragLeave()
+{
+ TRACE("(%p)\n", this);
+
+ fAcceptFmt = FALSE;
+
+ return S_OK;
+}
+
+HRESULT WINAPI CRecycleBin::Drop(IDataObject *pDataObject,
+ DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
+{
+ FIXME("(%p) object dropped on recycle bin, effect %u\n", this,
*pdwEffect);
+
+ /* TODO: pdwEffect should be read and make the drop object be permanently deleted in
the move case (shift held) */
+
+ FORMATETC fmt;
+ TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
+ InitFormatEtc (fmt, cfShellIDList, TYMED_HGLOBAL);
+
+ /* Handle cfShellIDList Drop objects here, otherwise send the approriate message to
other software */
+ if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) {
+ pDataObject->AddRef();
+ SHCreateThread(DoDeleteThreadProc, pDataObject, NULL, NULL);
+ }
+ else
+ {
+ /*
+ * TODO call SetData on the data object with format CFSTR_TARGETCLSID
+ * set to the Recycle Bin's class identifier CLSID_RecycleBin.
+ */
+ }
+ return S_OK;
+}
+
+DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter)
+{
+ IDataObject *pda = (IDataObject*) lpParameter;
+ DoDeleteDataObject(pda);
+ //Release the data object
+ pda->Release();
+ return 0;
+}
+
+HRESULT WINAPI DoDeleteDataObject(IDataObject *pda)
+{
+ TRACE("performing delete");
+ HRESULT hr;
+
+ STGMEDIUM medium;
+ FORMATETC formatetc;
+ InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST),
TYMED_HGLOBAL);
+ hr = pda->GetData(&formatetc, &medium);
+ if (FAILED(hr))
+ return hr;
+
+ /* lock the handle */
+ LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
+ if (!lpcida)
+ {
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ /* convert the data into pidl */
+ LPITEMIDLIST pidl;
+ LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
+ if (!apidl)
+ {
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ CComPtr<IShellFolder> psfDesktop;
+ CComPtr<IShellFolder> psfFrom = NULL;
+
+ /* Grab the desktop shell folder */
+ hr = SHGetDesktopFolder(&psfDesktop);
+ if (FAILED(hr))
+ {
+ ERR("SHGetDesktopFolder failed\n");
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ /* Find source folder, this is where the clipboard data was copied from */
+ if (_ILIsDesktop(pidl))
+ {
+ psfFrom = psfDesktop;
+ }
+ else
+ {
+ hr = psfDesktop->BindToObject(pidl, NULL, IID_IShellFolder,
(LPVOID*)&psfFrom);
+ if (FAILED(hr))
+ {
+ ERR("no IShellFolder\n");
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+ }
+
+ STRRET strTemp;
+ hr = psfFrom->GetDisplayNameOf(apidl[0], SHGDN_FORPARSING, &strTemp);
+ if (FAILED(hr))
+ {
+ ERR("IShellFolder_GetDisplayNameOf failed with %x\n", hr);
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+ return hr;
+ }
+
+ WCHAR wszPath[MAX_PATH];
+ hr = StrRetToBufW(&strTemp, apidl[0], wszPath, _countof(wszPath));
+ if (FAILED(hr))
+ {
+ ERR("StrRetToBufW failed with %x\n", hr);
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+ return hr;
+ }
+
+ /* Only keep the base path */
+ LPWSTR pwszFilename = PathFindFileNameW(wszPath);
+ *pwszFilename = L'\0';
+
+ /* Build paths list */
+ LPWSTR pwszPaths = BuildPathsList(wszPath, lpcida->cidl, (LPCITEMIDLIST*) apidl);
+ if (!pwszPaths)
+ {
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+ return E_FAIL;
+ }
+
+ /* Delete them */
+ SHFILEOPSTRUCTW FileOp;
+ ZeroMemory(&FileOp, sizeof(FileOp));
+ FileOp.wFunc = FO_DELETE;
+ FileOp.pFrom = pwszPaths;
+ FileOp.fFlags = FOF_ALLOWUNDO;
+
+ if (SHFileOperationW(&FileOp) != 0)
+ {
+ ERR("SHFileOperation failed with 0x%x for %s\n", GetLastError(),
debugstr_w(pwszPaths));
+ hr = E_FAIL;
+ }
+
+ HeapFree(GetProcessHeap(), 0, pwszPaths);
+ SHFree(pidl);
+ _ILFreeaPidl(apidl, lpcida->cidl);
+ ReleaseStgMedium(&medium);
+
+ return hr;
+}
Modified: trunk/reactos/dll/win32/shell32/folders/recyclebin.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/folders/…
==============================================================================
--- trunk/reactos/dll/win32/shell32/folders/recyclebin.h [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/folders/recyclebin.h [iso-8859-1] Sun Jan 5 12:48:42
2014
@@ -22,6 +22,9 @@
#ifndef _SHFLDR_RECYCLEBIN_H_
#define _SHFLDR_RECYCLEBIN_H_
+DWORD WINAPI DoDeleteThreadProc(LPVOID lpParameter);
+HRESULT WINAPI DoDeleteDataObject(IDataObject *pda);
+
class CRecycleBin :
public CComCoClass<CRecycleBin, &CLSID_RecycleBin>,
public CComObjectRootEx<CComMultiThreadModelNoCS>,
@@ -29,11 +32,16 @@
public IPersistFolder2,
public IContextMenu,
public IShellPropSheetExt,
+ public IDropTarget,
public IShellExtInit
{
private:
LPITEMIDLIST pidl;
INT iIdEmpty;
+ UINT cfShellIDList;
+ void SF_RegisterClipFmt();
+ BOOL fAcceptFmt; /* flag for pending Drop */
+ BOOL QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
public:
CRecycleBin();
@@ -75,6 +83,12 @@
// IShellPropSheetExt
virtual HRESULT WINAPI AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM
lParam);
virtual HRESULT WINAPI ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE
pfnReplaceWith, LPARAM lParam);
+
+ // IDropTarget
+ virtual HRESULT WINAPI DragEnter(IDataObject *pDataObject, DWORD dwKeyState,
POINTL pt, DWORD *pdwEffect);
+ virtual HRESULT WINAPI DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect);
+ virtual HRESULT WINAPI DragLeave();
+ virtual HRESULT WINAPI Drop(IDataObject *pDataObject, DWORD dwKeyState, POINTL
pt, DWORD *pdwEffect);
// IShellExtInit
virtual HRESULT STDMETHODCALLTYPE Initialize(LPCITEMIDLIST pidlFolder,
IDataObject *pdtobj, HKEY hkeyProgID);
@@ -91,6 +105,7 @@
COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
COM_INTERFACE_ENTRY_IID(IID_IShellPropSheetExt, IShellPropSheetExt)
+ COM_INTERFACE_ENTRY_IID(IID_IDropTarget, IDropTarget)
COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
END_COM_MAP()
};
Modified: trunk/reactos/dll/win32/shell32/shlview.cpp
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/shlview.…
==============================================================================
--- trunk/reactos/dll/win32/shell32/shlview.cpp [iso-8859-1] (original)
+++ trunk/reactos/dll/win32/shell32/shlview.cpp [iso-8859-1] Sun Jan 5 12:48:42 2014
@@ -1691,8 +1691,6 @@
{
DWORD dwEffect2;
DoDragDrop(pda, pds, dwEffect, &dwEffect2);
- if ((dwEffect2 & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
- this->Refresh();
}
pda->Release();
}