Skip to content

Commit 59e556f

Browse files
authored
[SHELL32] Implement DragDropHandlers shell extension context menus (reactos#8143)
CORE-11240 CORE-11238
1 parent 06414ac commit 59e556f

File tree

11 files changed

+234
-13
lines changed

11 files changed

+234
-13
lines changed

dll/win32/shell32/CDefaultContextMenu.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,112 @@ static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags)
3232
return InsertMenuItemW(hMenu, Pos, TRUE, &mii);
3333
}
3434

35+
static void DCMA_DestroyEntry(DCMENTRY &dcme)
36+
{
37+
if (!dcme.pCM)
38+
return;
39+
IUnknown_SetSite(dcme.pCM, NULL);
40+
dcme.pCM->Release();
41+
dcme.pCM = NULL;
42+
}
43+
44+
void DCMA_Destroy(HDCMA hDCMA)
45+
{
46+
UINT i = 0;
47+
for (DCMENTRY *p; (p = DCMA_GetEntry(hDCMA, i)) != NULL; ++i)
48+
DCMA_DestroyEntry(*p);
49+
DSA_Destroy(hDCMA);
50+
}
51+
52+
UINT DCMA_InsertMenuItems(
53+
_In_ HDCMA hDCMA,
54+
_In_ HDCIA hDCIA,
55+
_In_opt_ LPCITEMIDLIST pidlFolder,
56+
_In_opt_ IDataObject *pDO,
57+
_In_opt_ HKEY *pKeys,
58+
_In_opt_ UINT nKeys,
59+
_In_ QCMINFO *pQCMI,
60+
_In_opt_ UINT fCmf,
61+
_In_opt_ IUnknown *pUnkSite)
62+
{
63+
UINT idCmdBase = pQCMI->idCmdFirst, idCmdFirst = idCmdBase;
64+
UINT nOffset = 0;
65+
66+
// Insert in reverse order
67+
for (int iCls = DCIA_GetCount(hDCIA) - 1; iCls >= 0; --iCls)
68+
{
69+
REFCLSID clsid = *DCIA_GetEntry(hDCIA, iCls);
70+
if (fCmf & CMF_DEFAULTONLY)
71+
{
72+
WCHAR szKey[MAX_PATH];
73+
wcscpy(szKey, L"CLSID\\");
74+
StringFromGUID2(clsid, szKey + _countof(L"CLSID\\") - 1, CHARS_IN_GUID);
75+
wcscpy(szKey + _countof(L"CLSID\\") - 1 + CHARS_IN_GUID - 1, L"\\shellex\\MayChangeDefaultMenu");
76+
if (!RegKeyExists(HKEY_CLASSES_ROOT, szKey))
77+
continue;
78+
}
79+
80+
for (UINT iKey = 0; iKey < nKeys; ++iKey)
81+
{
82+
CComPtr<IShellExtInit> pInit;
83+
HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellExtInit, &pInit));
84+
if (FAILED(hr))
85+
break;
86+
if (FAILED(hr = pInit->Initialize(pidlFolder, pDO, pKeys[iKey])))
87+
continue;
88+
89+
IContextMenu *pCM;
90+
if (FAILED(hr = pInit->QueryInterface(IID_PPV_ARG(IContextMenu, &pCM))))
91+
break;
92+
IUnknown_SetSite(pCM, pUnkSite);
93+
94+
hr = pCM->QueryContextMenu(pQCMI->hmenu, pQCMI->indexMenu + nOffset, idCmdFirst, pQCMI->idCmdLast, fCmf);
95+
const UINT nCount = HRESULT_CODE(hr);
96+
const UINT idThisFirst = idCmdFirst - idCmdBase;
97+
DCMENTRY dcme = { pCM, idThisFirst, idThisFirst + nCount - 1 };
98+
if (hr > 0)
99+
{
100+
idCmdFirst += nCount;
101+
if (DSA_AppendItem(hDCMA, &dcme) >= 0)
102+
{
103+
if (nOffset == 0 && GetMenuDefaultItem(pQCMI->hmenu, TRUE, 0) == 0)
104+
nOffset++; // Insert new items below the default
105+
break;
106+
}
107+
}
108+
DCMA_DestroyEntry(dcme);
109+
}
110+
}
111+
return idCmdFirst;
112+
}
113+
114+
HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI)
115+
{
116+
HRESULT hr = S_FALSE;
117+
for (UINT i = 0;; ++i)
118+
{
119+
DCMENTRY *p = DCMA_GetEntry(hDCMA, i);
120+
if (!p)
121+
return hr;
122+
123+
UINT id = LOWORD(pICI->lpVerb);
124+
if (!IS_INTRESOURCE(pICI->lpVerb))
125+
{
126+
if (SUCCEEDED(hr = p->pCM->InvokeCommand(pICI)))
127+
return hr;
128+
}
129+
else if (id >= p->idCmdFirst && id <= p->idCmdLast)
130+
{
131+
CMINVOKECOMMANDINFOEX ici;
132+
CopyMemory(&ici, pICI, min(sizeof(ici), pICI->cbSize));
133+
ici.cbSize = min(sizeof(ici), pICI->cbSize);
134+
ici.lpVerb = MAKEINTRESOURCEA(id - p->idCmdFirst);
135+
ici.lpVerbW = (PWSTR)ici.lpVerb;
136+
return p->pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&ici);
137+
}
138+
}
139+
}
140+
35141
typedef struct _DynamicShellEntry_
36142
{
37143
UINT iIdCmdFirst;

dll/win32/shell32/droptargets/CFSDropTarget.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,6 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
237237
HMENU hmenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(IDM_DRAGFILE));
238238
if (!hmenu)
239239
return E_OUTOFMEMORY;
240-
241240
HMENU hpopupmenu = GetSubMenu(hmenu, 0);
242241

243242
SHELL_LimitDropEffectToItemAttributes(pDataObject, &dwAvailableEffects);
@@ -256,7 +255,19 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
256255
else if (dwAvailableEffects & DROPEFFECT_LINK)
257256
SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE);
258257

259-
/* FIXME: We need to support shell extensions here */
258+
CRegKeyHandleArray keys;
259+
HDCIA hDCIA = DCIA_Create();
260+
HDCMA hDCMA = DCMA_Create();
261+
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlFolder(SHELL32_CreateSimpleIDListFromPath(m_sPathTarget, FILE_ATTRIBUTE_DIRECTORY));
262+
if (hDCMA && hDCIA && pidlFolder)
263+
{
264+
AddPidlClassKeysToArray(pidlFolder, keys, keys);
265+
for (UINT i = 0; i < keys; ++i)
266+
DCIA_AddShellExSubkey(hDCIA, keys[i], L"DragDropHandlers");
267+
268+
QCMINFO qcmi = { hpopupmenu, 0, DROPIDM_EXTFIRST, DROPIDM_EXTLAST };
269+
DCMA_InsertMenuItems(hDCMA, hDCIA, pidlFolder, pDataObject, keys, keys, &qcmi, 0, m_site);
270+
}
260271

261272
/* We shouldn't use the site window here because the menu should work even when we don't have a site */
262273
HWND hwndDummy = CreateWindowEx(0,
@@ -279,16 +290,39 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
279290

280291
DestroyWindow(hwndDummy);
281292

282-
if (uCommand == 0)
283-
return S_FALSE;
293+
HRESULT hr = S_FALSE; // S_FALSE means we did not handle the command
294+
C_ASSERT(IDM_COPYHERE < DROPIDM_EXTFIRST && IDM_MOVEHERE < DROPIDM_EXTFIRST &&
295+
IDM_LINKHERE < DROPIDM_EXTFIRST && DROPIDM_EXTFIRST > 0);
296+
if (uCommand >= DROPIDM_EXTFIRST && uCommand <= DROPIDM_EXTLAST)
297+
{
298+
CMINVOKECOMMANDINFO ici = { sizeof(ici), 0, m_hwndSite, MAKEINTRESOURCEA(uCommand - DROPIDM_EXTFIRST) };
299+
ici.nShow = SW_SHOW;
300+
if (m_grfKeyState & MK_SHIFT)
301+
ici.fMask |= CMIC_MASK_SHIFT_DOWN;
302+
if (m_grfKeyState & MK_CONTROL)
303+
ici.fMask |= CMIC_MASK_CONTROL_DOWN;
304+
DCMA_InvokeCommand(hDCMA, &ici);
305+
hr = S_OK;
306+
*pdwEffect = DROPEFFECT_NONE;
307+
}
308+
else if (uCommand == 0)
309+
{
310+
hr = S_OK;
311+
*pdwEffect = DROPEFFECT_NONE;
312+
}
284313
else if (uCommand == IDM_COPYHERE)
285314
*pdwEffect = DROPEFFECT_COPY;
286315
else if (uCommand == IDM_MOVEHERE)
287316
*pdwEffect = DROPEFFECT_MOVE;
288317
else if (uCommand == IDM_LINKHERE)
289318
*pdwEffect = DROPEFFECT_LINK;
319+
else
320+
hr = E_UNEXPECTED;
290321

291-
return S_OK;
322+
DCMA_Destroy(hDCMA);
323+
DCIA_Destroy(hDCIA);
324+
DestroyMenu(hmenu);
325+
return hr;
292326
}
293327

294328
HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt)
@@ -461,7 +495,7 @@ HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject,
461495
if (m_grfKeyState & MK_RBUTTON)
462496
{
463497
HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects);
464-
if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE)
498+
if (FAILED_UNEXPECTEDLY(hr) || hr == S_OK)
465499
return hr;
466500
}
467501

dll/win32/shell32/droptargets/CFSDropTarget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
#ifndef _CFSDROPTARGET_H_
2424
#define _CFSDROPTARGET_H_
2525

26+
#define DROPIDM_EXTFIRST 100
27+
#define DROPIDM_EXTLAST 0x7fff
28+
2629
class CFSDropTarget :
2730
public CComObjectRootEx<CComMultiThreadModelNoCS>,
2831
public IDropTarget,

dll/win32/shell32/precomp.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,30 @@ SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO);
179179
HRESULT
180180
SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl);
181181

182+
typedef HDSA HDCMA; // DynamicContextMenuArray
183+
typedef struct _DCMENTRY
184+
{
185+
IContextMenu *pCM;
186+
UINT idCmdFirst;
187+
UINT idCmdLast;
188+
} DCMENTRY;
189+
#define DCMA_Create() ( (HDCMA)DSA_Create(sizeof(DCMENTRY), 4) )
190+
void DCMA_Destroy(HDCMA hDCMA);
191+
#define DCMA_GetEntry(hDCMA, iItem) ( (DCMENTRY*)DSA_GetItemPtr((HDSA)(hDCMA), (iItem)) )
192+
HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI);
193+
194+
UINT
195+
DCMA_InsertMenuItems(
196+
_In_ HDCMA hDCMA,
197+
_In_ HDCIA hDCIA,
198+
_In_opt_ LPCITEMIDLIST pidlFolder,
199+
_In_opt_ IDataObject *pDO,
200+
_In_opt_ HKEY *pKeys,
201+
_In_opt_ UINT nKeys,
202+
_In_ QCMINFO *pQCMI,
203+
_In_opt_ UINT fCmf,
204+
_In_opt_ IUnknown *pUnkSite);
205+
182206
HRESULT
183207
SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg);
184208
UINT

dll/win32/shell32/propsheet.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99

1010
WINE_DEFAULT_DEBUG_CHANNEL(shell);
1111

12-
static HRESULT
13-
SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID &clsid)
12+
HRESULT
13+
SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId)
1414
{
1515
// First try the key name
16-
if (SUCCEEDED(SHCLSIDFromStringW(KeyName, &clsid)))
16+
if (SUCCEEDED(SHCLSIDFromStringW(KeyName, pClsId)))
1717
return S_OK;
1818
WCHAR buf[42];
1919
DWORD cb = sizeof(buf);
2020
// and then the default value
2121
DWORD err = RegGetValueW(hKey, KeyName, NULL, RRF_RT_REG_SZ, NULL, buf, &cb);
22-
return !err ? SHCLSIDFromStringW(buf, &clsid) : HRESULT_FROM_WIN32(err);
22+
return !err ? SHCLSIDFromStringW(buf, pClsId) : HRESULT_FROM_WIN32(err);
2323
}
2424

2525
static HRESULT
@@ -148,7 +148,7 @@ SHELL32_OpenPropSheet(LPCWSTR pszCaption, HKEY *ahKeys, UINT cKeys,
148148
if (err)
149149
break;
150150
CLSID clsid;
151-
if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, clsid)))
151+
if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, &clsid)))
152152
AddPropSheetHandlerPages(clsid, pDO, hKeyProgID, psh);
153153
}
154154
RegCloseKey(hKey);

dll/win32/shell32/shell32.spec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,5 +466,5 @@
466466
754 stub -noname SHLimitInputEditWithFlags
467467
755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr)
468468
756 stub -noname DeleteFileThumbnail
469-
757 stdcall -version=0x600+ DisplayNameOfW(ptr ptr long ptr long)
470-
866 stdcall -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr)
469+
757 stdcall -noname -version=0x600+ DisplayNameOfW(ptr ptr long ptr long)
470+
866 stdcall -noname -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr)

dll/win32/shell32/shfldr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ void CloseRegKeyArray(HKEY* array, UINT cKeys);
184184
LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys);
185185
LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys);
186186
void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys);
187+
void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys);
187188

188189
#ifdef __cplusplus
189190

dll/win32/shell32/shlfolder.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,23 @@ void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array,
490490
AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys);
491491
AddClassKeyToArray(L"Directory", array, cKeys);
492492
}
493+
else if (_ILIsDrive(pidl))
494+
{
495+
AddClassKeyToArray(L"Drive", array, cKeys);
496+
AddClassKeyToArray(L"Folder", array, cKeys);
497+
}
493498
else
494499
{
495500
ERR("Got non FS pidl\n");
496501
}
497502
}
498503

504+
void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys)
505+
{
506+
if ((pidl = ILFindLastID(pidl)) != NULL)
507+
AddFSClassKeysToArray(1, &pidl, array, cKeys);
508+
}
509+
499510
HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl)
500511
{
501512
CDataObjectHIDA cida(pDataObject);

dll/win32/shell32/utils.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,6 +1836,35 @@ SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut
18361836
return initIcon->QueryInterface(riid, ppvOut);
18371837
}
18381838

1839+
int DCIA_AddEntry(HDCIA hDCIA, REFCLSID rClsId)
1840+
{
1841+
for (UINT i = 0;; ++i)
1842+
{
1843+
const CLSID *pClsId = DCIA_GetEntry(hDCIA, i);
1844+
if (!pClsId)
1845+
break;
1846+
if (IsEqualGUID(*pClsId, rClsId))
1847+
return i; // Don't allow duplicates
1848+
}
1849+
return DSA_AppendItem((HDSA)hDCIA, const_cast<CLSID*>(&rClsId));
1850+
}
1851+
1852+
void DCIA_AddShellExSubkey(HDCIA hDCIA, HKEY hProgId, PCWSTR pszSubkey)
1853+
{
1854+
WCHAR szKey[200];
1855+
PathCombineW(szKey, L"shellex", pszSubkey);
1856+
HKEY hEnum;
1857+
if (RegOpenKeyExW(hProgId, szKey, 0, KEY_READ, &hEnum) != ERROR_SUCCESS)
1858+
return;
1859+
for (UINT i = 0; RegEnumKeyW(hEnum, i++, szKey, _countof(szKey)) == ERROR_SUCCESS;)
1860+
{
1861+
CLSID clsid;
1862+
if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hEnum, szKey, &clsid)))
1863+
DCIA_AddEntry(hDCIA, clsid);
1864+
}
1865+
RegCloseKey(hEnum);
1866+
}
1867+
18391868
/*************************************************************************
18401869
* SHIsBadInterfacePtr [SHELL32.84]
18411870
*

dll/win32/shell32/utils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut)
120120
return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
121121
}
122122

123+
typedef HDSA HDCIA; // DynamicClassIdArray
124+
#define DCIA_Create() ( (HDCIA)DSA_Create(sizeof(CLSID), 4) )
125+
#define DCIA_Destroy(hDCIA) DSA_Destroy((HDSA)(hDCIA))
126+
#define DCIA_GetCount(hDCIA) DSA_GetItemCount((HDSA)(hDCIA))
127+
#define DCIA_GetEntry(hDCIA, iItem) ( (const CLSID*)DSA_GetItemPtr((HDSA)(hDCIA), (iItem)) )
128+
int DCIA_AddEntry(HDCIA hDCIA, REFCLSID rClsId);
129+
void DCIA_AddShellExSubkey(HDCIA hDCIA, HKEY hProgId, PCWSTR pszSubkey);
130+
123131
#ifdef __cplusplus
124132
struct ClipboardViewerChain
125133
{

0 commit comments

Comments
 (0)