[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

svn path=/trunk/; revision=61537
This commit is contained in:
Amine Khaldi 2014-01-05 12:48:42 +00:00
parent aa4d139a0c
commit 3ad827aa50
6 changed files with 293 additions and 69 deletions

View file

@ -1037,7 +1037,6 @@ CDefaultContextMenu::DoPaste(
}
SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
NotifyShellViewWindow(lpcmi, TRUE);
TRACE("CP result %x\n", hr);
return S_OK;
@ -1104,73 +1103,24 @@ CDefaultContextMenu::DoCreateLink(
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)
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))))
{
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)
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;
}

View file

@ -1419,6 +1419,27 @@ HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
{
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 @@ HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
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 @@ HRESULT WINAPI CFSFolder::Drop(IDataObject *pDataObject,
}
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;
}

View file

@ -46,7 +46,8 @@ class CFSFolder :
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 @@ class CFSFolder :
END_COM_MAP()
};
struct _DoDropData {
CFSFolder *This;
IDataObject *pDataObject;
DWORD dwKeyState;
POINTL pt;
DWORD pdwEffect;
};
#endif // _CFSFOLDER_H_

View file

@ -429,10 +429,24 @@ static HRESULT WINAPI CRecycleBinItemContextMenuConstructor(REFIID riid, LPCITEM
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 @@ HRESULT WINAPI CRecycleBin::CreateViewObject(HWND hwndOwner, REFIID riid, void *
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 @@ HRESULT WINAPI CRecycleBin::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLI
{
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 @@ EXTERN_C HRESULT WINAPI SHUpdateRecycleBinIcon(void)
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;
}

View file

@ -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 @@ class CRecycleBin :
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 @@ class CRecycleBin :
// 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 @@ class CRecycleBin :
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()
};

View file

@ -1691,8 +1691,6 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl
{
DWORD dwEffect2;
DoDragDrop(pda, pds, dwEffect, &dwEffect2);
if ((dwEffect2 & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
this->Refresh();
}
pda->Release();
}