[PATCH] shell32: support the CF_HDROP format too when pasting from the context menu

Damjan Jovanovic damjan.jov at gmail.com
Tue May 4 01:34:02 CDT 2021


Signed-off-by: Damjan Jovanovic <damjan.jov at gmail.com>
---
 dlls/shell32/shlview_cmenu.c   | 145 ++++++++++++++++++++++-----------
 dlls/shell32/tests/shlfolder.c |  10 +++
 2 files changed, 107 insertions(+), 48 deletions(-)
-------------- next part --------------
diff --git a/dlls/shell32/shlview_cmenu.c b/dlls/shell32/shlview_cmenu.c
index 67f5ac80d84..e61ad1495f6 100644
--- a/dlls/shell32/shlview_cmenu.c
+++ b/dlls/shell32/shlview_cmenu.c
@@ -1134,17 +1134,67 @@ static void DoNewFolder(ContextMenu *This, IShellView *view)
     }
 }
 
-static BOOL DoPaste(ContextMenu *This)
+static HRESULT paste_pidls(ContextMenu *This, ITEMIDLIST **pidls, UINT count)
+{
+    IShellFolder *psfDesktop;
+    UINT i;
+    HRESULT hr = S_OK;
+
+    /* bind to the source shellfolder */
+    hr = SHGetDesktopFolder(&psfDesktop);
+    if (FAILED(hr))
+        return hr;
+
+    for (i = 0; SUCCEEDED(hr) && i < count; i++) {
+        ITEMIDLIST *pidl_dir = NULL;
+        ITEMIDLIST *pidl_item;
+        IShellFolder *psfFrom = NULL;
+
+        pidl_dir = ILClone(pidls[i]);
+        ILRemoveLastID(pidl_dir);
+        pidl_item = ILFindLastID(pidls[i]);
+        hr = IShellFolder_BindToObject(psfDesktop, pidl_dir, NULL, &IID_IShellFolder, (LPVOID*)&psfFrom);
+
+        if (psfFrom)
+        {
+            /* get source and destination shellfolder */
+            ISFHelper *psfhlpdst, *psfhlpsrc;
+            hr = IShellFolder_QueryInterface(This->parent, &IID_ISFHelper, (void**)&psfhlpdst);
+            if (SUCCEEDED(hr))
+                hr = IShellFolder_QueryInterface(psfFrom, &IID_ISFHelper, (void**)&psfhlpsrc);
+
+            /* do the copy/move */
+            if (psfhlpdst && psfhlpsrc)
+            {
+                hr = ISFHelper_CopyItems(psfhlpdst, psfFrom, 1, (LPCITEMIDLIST*)&pidl_item);
+                /* FIXME handle move
+                ISFHelper_DeleteItems(psfhlpsrc, 1, &pidl_item);
+                */
+            }
+            if(psfhlpdst) ISFHelper_Release(psfhlpdst);
+            if(psfhlpsrc) ISFHelper_Release(psfhlpsrc);
+            IShellFolder_Release(psfFrom);
+        }
+        SHFree(pidl_dir);
+    }
+
+    IShellFolder_Release(psfDesktop);
+    return hr;
+}
+
+static HRESULT DoPaste(ContextMenu *This)
 {
-	BOOL bSuccess = TRUE;
 	IDataObject * pda;
+	HRESULT hr;
 
 	TRACE("\n");
 
-	if(SUCCEEDED(OleGetClipboard(&pda)))
+	hr = OleGetClipboard(&pda);
+	if(SUCCEEDED(hr))
 	{
 	  STGMEDIUM medium;
 	  FORMATETC formatetc;
+	  HRESULT format_hr;
 
 	  TRACE("pda=%p\n", pda);
 
@@ -1152,72 +1202,71 @@ static BOOL DoPaste(ContextMenu *This)
 	  InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLISTW), TYMED_HGLOBAL);
 
 	  /* Get the pidls from IDataObject */
-	  if(SUCCEEDED(IDataObject_GetData(pda,&formatetc,&medium)))
+	  format_hr = IDataObject_GetData(pda,&formatetc,&medium);
+	  if(SUCCEEDED(format_hr))
           {
 	    LPITEMIDLIST * apidl;
 	    LPITEMIDLIST pidl;
-	    IShellFolder *psfFrom = NULL, *psfDesktop;
-	    int i;
 
 	    LPIDA lpcida = GlobalLock(medium.u.hGlobal);
 	    TRACE("cida=%p\n", lpcida);
 
 	    apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
+	    hr = paste_pidls(This, apidl, lpcida->cidl);
 
-	    for (i = 0; bSuccess && i < lpcida->cidl; i++) {
-	      ITEMIDLIST *apidl_dir = NULL;
-	      ITEMIDLIST *apidl_item;
+	    _ILFreeaPidl(apidl, lpcida->cidl);
+	    SHFree(pidl);
 
-	      psfFrom = NULL;
-	      /* bind to the source shellfolder */
-	      SHGetDesktopFolder(&psfDesktop);
-	      if(psfDesktop)
-	      {
-	        apidl_dir = ILClone(apidl[i]);
-	        ILRemoveLastID(apidl_dir);
-	        apidl_item = ILFindLastID(apidl[i]);
-	        IShellFolder_BindToObject(psfDesktop, apidl_dir, NULL, &IID_IShellFolder, (LPVOID*)&psfFrom);
-	        IShellFolder_Release(psfDesktop);
-	      }
+	    /* release the medium*/
+	    ReleaseStgMedium(&medium);
+	  }
 
-	      if (psfFrom)
+	  if(FAILED(format_hr))
+	  {
+	    InitFormatEtc(formatetc, CF_HDROP, TYMED_HGLOBAL);
+	    format_hr = IDataObject_GetData(pda,&formatetc,&medium);
+	    if(SUCCEEDED(format_hr))
+	    {
+	      DROPFILES *dropfiles = GlobalLock(medium.u.hGlobal);
+	      TRACE("CF_HDROP=%p\n", dropfiles);
+	      if (dropfiles)
 	      {
-	        /* get source and destination shellfolder */
-	        ISFHelper *psfhlpdst, *psfhlpsrc;
-	        IShellFolder_QueryInterface(This->parent, &IID_ISFHelper, (void**)&psfhlpdst);
-	        IShellFolder_QueryInterface(psfFrom, &IID_ISFHelper, (void**)&psfhlpsrc);
+	        WCHAR path[MAX_PATH];
+	        UINT i, count;
+	        ITEMIDLIST **pidls;
 
-	        /* do the copy/move */
-	        if (psfhlpdst && psfhlpsrc)
+	        count = DragQueryFileW(medium.u.hGlobal, -1, NULL, 0);
+	        pidls = SHAlloc(count*sizeof(ITEMIDLIST**));
+	        if (pidls)
 	        {
-	          HRESULT hr = ISFHelper_CopyItems(psfhlpdst, psfFrom, 1, (LPCITEMIDLIST*)&apidl_item);
-	          if (FAILED(hr))
-	            bSuccess = FALSE;
-		  /* FIXME handle move
-		  ISFHelper_DeleteItems(psfhlpsrc, 1, &apidl_item);
-	          */
+	          for (i = 0; i < count; i++)
+	          {
+	            DragQueryFileW(medium.u.hGlobal, i, path, ARRAY_SIZE(path));
+	            if ((pidls[i] = ILCreateFromPathW(path)) == NULL)
+	            {
+	              hr = E_FAIL;
+	              break;
+	            }
+	          }
+	          if (SUCCEEDED(hr))
+	            hr = paste_pidls(This, pidls, count);
+	          _ILFreeaPidl(pidls, count);
 	        }
-	        if(psfhlpdst) ISFHelper_Release(psfhlpdst);
-	        if(psfhlpsrc) ISFHelper_Release(psfhlpsrc);
-	        IShellFolder_Release(psfFrom);
+	        else
+	          hr = HRESULT_FROM_WIN32(GetLastError());
 	      }
 	      else
-	        bSuccess = FALSE;
-	      SHFree(apidl_dir);
+	        hr = HRESULT_FROM_WIN32(GetLastError());
 	    }
+	  }
 
-	    _ILFreeaPidl(apidl, lpcida->cidl);
-	    SHFree(pidl);
-
-	    /* release the medium*/
-	    ReleaseStgMedium(&medium);
+	  if (FAILED(format_hr))
+	  {
+	    ERR("there are no supported and retrievable clipboard formats\n");
+	    hr = format_hr;
 	  }
-	  else
-	    bSuccess = FALSE;
 	  IDataObject_Release(pda);
 	}
-	else
-	  bSuccess = FALSE;
 #if 0
 	HGLOBAL  hMem;
 
@@ -1242,7 +1291,7 @@ static BOOL DoPaste(ContextMenu *This)
 	}
 	CloseClipboard();
 #endif
-	return bSuccess;
+	return hr;
 }
 
 static HRESULT WINAPI BackgroundMenu_InvokeCommand(
diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c
index 51776f050ae..c3cbe8b2bc2 100644
--- a/dlls/shell32/tests/shlfolder.c
+++ b/dlls/shell32/tests/shlfolder.c
@@ -4387,7 +4387,17 @@ static void test_contextmenu(IContextMenu *menu, BOOL background)
             {
                 trace("Got ID %d, verb %s, string %s.\n", mii.wID, debugstr_a(buf), debugstr_a(mii.dwTypeData));
                 if (!strcmp(buf, "copy"))
+                {
+                    CMINVOKECOMMANDINFO cmi;
                     ok(mii.wID == 64 - 0x7000 + FCIDM_SHVIEW_COPY, "wrong menu wID %d\n", mii.wID);
+                    memset(&cmi, 0, sizeof(CMINVOKECOMMANDINFO));
+                    cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
+                    cmi.lpVerb = "copy";
+                    hr = IContextMenu_InvokeCommand(menu, &cmi);
+                    ok(hr == S_OK, "got 0x%08x\n", hr);
+                    ok(IsClipboardFormatAvailable(RegisterClipboardFormatA(CFSTR_SHELLIDLISTA)), "CFSTR_SHELLIDLISTA not available\n");
+                    ok(IsClipboardFormatAvailable(CF_HDROP), "CF_HDROP not available\n");
+                }
                 else if (!strcmp(buf, "paste"))
                     ok(mii.wID == 64 - 0x7000 + FCIDM_SHVIEW_INSERT, "wrong menu wID %d\n", mii.wID);
                 else if (!strcmp(buf, "properties"))


More information about the wine-devel mailing list