diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index c815e75fb75..554aeedbc01 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -32,6 +32,112 @@ static BOOL InsertMenuItemAt(HMENU hMenu, UINT Pos, UINT Flags) return InsertMenuItemW(hMenu, Pos, TRUE, &mii); } +static void DCMA_DestroyEntry(DCMENTRY &dcme) +{ + if (!dcme.pCM) + return; + IUnknown_SetSite(dcme.pCM, NULL); + dcme.pCM->Release(); + dcme.pCM = NULL; +} + +void DCMA_Destroy(HDCMA hDCMA) +{ + UINT i = 0; + for (DCMENTRY *p; (p = DCMA_GetEntry(hDCMA, i)) != NULL; ++i) + DCMA_DestroyEntry(*p); + DSA_Destroy(hDCMA); +} + +UINT DCMA_InsertMenuItems( + _In_ HDCMA hDCMA, + _In_ HDCIA hDCIA, + _In_opt_ LPCITEMIDLIST pidlFolder, + _In_opt_ IDataObject *pDO, + _In_opt_ HKEY *pKeys, + _In_opt_ UINT nKeys, + _In_ QCMINFO *pQCMI, + _In_opt_ UINT fCmf, + _In_opt_ IUnknown *pUnkSite) +{ + UINT idCmdBase = pQCMI->idCmdFirst, idCmdFirst = idCmdBase; + UINT nOffset = 0; + + // Insert in reverse order + for (int iCls = DCIA_GetCount(hDCIA) - 1; iCls >= 0; --iCls) + { + REFCLSID clsid = *DCIA_GetEntry(hDCIA, iCls); + if (fCmf & CMF_DEFAULTONLY) + { + WCHAR szKey[MAX_PATH]; + wcscpy(szKey, L"CLSID\\"); + StringFromGUID2(clsid, szKey + _countof(L"CLSID\\") - 1, CHARS_IN_GUID); + wcscpy(szKey + _countof(L"CLSID\\") - 1 + CHARS_IN_GUID - 1, L"\\shellex\\MayChangeDefaultMenu"); + if (!RegKeyExists(HKEY_CLASSES_ROOT, szKey)) + continue; + } + + for (UINT iKey = 0; iKey < nKeys; ++iKey) + { + CComPtr pInit; + HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellExtInit, &pInit)); + if (FAILED(hr)) + break; + if (FAILED(hr = pInit->Initialize(pidlFolder, pDO, pKeys[iKey]))) + continue; + + IContextMenu *pCM; + if (FAILED(hr = pInit->QueryInterface(IID_PPV_ARG(IContextMenu, &pCM)))) + break; + IUnknown_SetSite(pCM, pUnkSite); + + hr = pCM->QueryContextMenu(pQCMI->hmenu, pQCMI->indexMenu + nOffset, idCmdFirst, pQCMI->idCmdLast, fCmf); + const UINT nCount = HRESULT_CODE(hr); + const UINT idThisFirst = idCmdFirst - idCmdBase; + DCMENTRY dcme = { pCM, idThisFirst, idThisFirst + nCount - 1 }; + if (hr > 0) + { + idCmdFirst += nCount; + if (DSA_AppendItem(hDCMA, &dcme) >= 0) + { + if (nOffset == 0 && GetMenuDefaultItem(pQCMI->hmenu, TRUE, 0) == 0) + nOffset++; // Insert new items below the default + break; + } + } + DCMA_DestroyEntry(dcme); + } + } + return idCmdFirst; +} + +HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI) +{ + HRESULT hr = S_FALSE; + for (UINT i = 0;; ++i) + { + DCMENTRY *p = DCMA_GetEntry(hDCMA, i); + if (!p) + return hr; + + UINT id = LOWORD(pICI->lpVerb); + if (!IS_INTRESOURCE(pICI->lpVerb)) + { + if (SUCCEEDED(hr = p->pCM->InvokeCommand(pICI))) + return hr; + } + else if (id >= p->idCmdFirst && id <= p->idCmdLast) + { + CMINVOKECOMMANDINFOEX ici; + CopyMemory(&ici, pICI, min(sizeof(ici), pICI->cbSize)); + ici.cbSize = min(sizeof(ici), pICI->cbSize); + ici.lpVerb = MAKEINTRESOURCEA(id - p->idCmdFirst); + ici.lpVerbW = (PWSTR)ici.lpVerb; + return p->pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&ici); + } + } +} + typedef struct _DynamicShellEntry_ { UINT iIdCmdFirst; diff --git a/dll/win32/shell32/droptargets/CFSDropTarget.cpp b/dll/win32/shell32/droptargets/CFSDropTarget.cpp index 458886bb5a6..d7542266847 100644 --- a/dll/win32/shell32/droptargets/CFSDropTarget.cpp +++ b/dll/win32/shell32/droptargets/CFSDropTarget.cpp @@ -237,7 +237,6 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D HMENU hmenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(IDM_DRAGFILE)); if (!hmenu) return E_OUTOFMEMORY; - HMENU hpopupmenu = GetSubMenu(hmenu, 0); SHELL_LimitDropEffectToItemAttributes(pDataObject, &dwAvailableEffects); @@ -256,7 +255,19 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D else if (dwAvailableEffects & DROPEFFECT_LINK) SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE); - /* FIXME: We need to support shell extensions here */ + CRegKeyHandleArray keys; + HDCIA hDCIA = DCIA_Create(); + HDCMA hDCMA = DCMA_Create(); + CComHeapPtr pidlFolder(SHELL32_CreateSimpleIDListFromPath(m_sPathTarget, FILE_ATTRIBUTE_DIRECTORY)); + if (hDCMA && hDCIA && pidlFolder) + { + AddPidlClassKeysToArray(pidlFolder, keys, keys); + for (UINT i = 0; i < keys; ++i) + DCIA_AddShellExSubkey(hDCIA, keys[i], L"DragDropHandlers"); + + QCMINFO qcmi = { hpopupmenu, 0, DROPIDM_EXTFIRST, DROPIDM_EXTLAST }; + DCMA_InsertMenuItems(hDCMA, hDCIA, pidlFolder, pDataObject, keys, keys, &qcmi, 0, m_site); + } /* We shouldn't use the site window here because the menu should work even when we don't have a site */ HWND hwndDummy = CreateWindowEx(0, @@ -279,16 +290,39 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D DestroyWindow(hwndDummy); - if (uCommand == 0) - return S_FALSE; + HRESULT hr = S_FALSE; // S_FALSE means we did not handle the command + C_ASSERT(IDM_COPYHERE < DROPIDM_EXTFIRST && IDM_MOVEHERE < DROPIDM_EXTFIRST && + IDM_LINKHERE < DROPIDM_EXTFIRST && DROPIDM_EXTFIRST > 0); + if (uCommand >= DROPIDM_EXTFIRST && uCommand <= DROPIDM_EXTLAST) + { + CMINVOKECOMMANDINFO ici = { sizeof(ici), 0, m_hwndSite, MAKEINTRESOURCEA(uCommand - DROPIDM_EXTFIRST) }; + ici.nShow = SW_SHOW; + if (m_grfKeyState & MK_SHIFT) + ici.fMask |= CMIC_MASK_SHIFT_DOWN; + if (m_grfKeyState & MK_CONTROL) + ici.fMask |= CMIC_MASK_CONTROL_DOWN; + DCMA_InvokeCommand(hDCMA, &ici); + hr = S_OK; + *pdwEffect = DROPEFFECT_NONE; + } + else if (uCommand == 0) + { + hr = S_OK; + *pdwEffect = DROPEFFECT_NONE; + } else if (uCommand == IDM_COPYHERE) *pdwEffect = DROPEFFECT_COPY; else if (uCommand == IDM_MOVEHERE) *pdwEffect = DROPEFFECT_MOVE; else if (uCommand == IDM_LINKHERE) *pdwEffect = DROPEFFECT_LINK; + else + hr = E_UNEXPECTED; - return S_OK; + DCMA_Destroy(hDCMA); + DCIA_Destroy(hDCIA); + DestroyMenu(hmenu); + return hr; } HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt) @@ -461,7 +495,7 @@ HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, if (m_grfKeyState & MK_RBUTTON) { HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects); - if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE) + if (FAILED_UNEXPECTEDLY(hr) || hr == S_OK) return hr; } diff --git a/dll/win32/shell32/droptargets/CFSDropTarget.h b/dll/win32/shell32/droptargets/CFSDropTarget.h index bde59979c24..8e6f4189c5e 100644 --- a/dll/win32/shell32/droptargets/CFSDropTarget.h +++ b/dll/win32/shell32/droptargets/CFSDropTarget.h @@ -23,6 +23,9 @@ #ifndef _CFSDROPTARGET_H_ #define _CFSDROPTARGET_H_ +#define DROPIDM_EXTFIRST 100 +#define DROPIDM_EXTLAST 0x7fff + class CFSDropTarget : public CComObjectRootEx, public IDropTarget, diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index bca0bef06e6..3d0bda0ec5e 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -179,6 +179,30 @@ SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO); HRESULT SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl); +typedef HDSA HDCMA; // DynamicContextMenuArray +typedef struct _DCMENTRY +{ + IContextMenu *pCM; + UINT idCmdFirst; + UINT idCmdLast; +} DCMENTRY; +#define DCMA_Create() ( (HDCMA)DSA_Create(sizeof(DCMENTRY), 4) ) +void DCMA_Destroy(HDCMA hDCMA); +#define DCMA_GetEntry(hDCMA, iItem) ( (DCMENTRY*)DSA_GetItemPtr((HDSA)(hDCMA), (iItem)) ) +HRESULT DCMA_InvokeCommand(HDCMA hDCMA, CMINVOKECOMMANDINFO *pICI); + +UINT +DCMA_InsertMenuItems( + _In_ HDCMA hDCMA, + _In_ HDCIA hDCIA, + _In_opt_ LPCITEMIDLIST pidlFolder, + _In_opt_ IDataObject *pDO, + _In_opt_ HKEY *pKeys, + _In_opt_ UINT nKeys, + _In_ QCMINFO *pQCMI, + _In_opt_ UINT fCmf, + _In_opt_ IUnknown *pUnkSite); + HRESULT SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg); UINT diff --git a/dll/win32/shell32/propsheet.cpp b/dll/win32/shell32/propsheet.cpp index a24faf3314e..c90ee679b01 100644 --- a/dll/win32/shell32/propsheet.cpp +++ b/dll/win32/shell32/propsheet.cpp @@ -9,17 +9,17 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); -static HRESULT -SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID &clsid) +HRESULT +SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId) { // First try the key name - if (SUCCEEDED(SHCLSIDFromStringW(KeyName, &clsid))) + if (SUCCEEDED(SHCLSIDFromStringW(KeyName, pClsId))) return S_OK; WCHAR buf[42]; DWORD cb = sizeof(buf); // and then the default value DWORD err = RegGetValueW(hKey, KeyName, NULL, RRF_RT_REG_SZ, NULL, buf, &cb); - return !err ? SHCLSIDFromStringW(buf, &clsid) : HRESULT_FROM_WIN32(err); + return !err ? SHCLSIDFromStringW(buf, pClsId) : HRESULT_FROM_WIN32(err); } static HRESULT @@ -148,7 +148,7 @@ SHELL32_OpenPropSheet(LPCWSTR pszCaption, HKEY *ahKeys, UINT cKeys, if (err) break; CLSID clsid; - if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, clsid))) + if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, &clsid))) AddPropSheetHandlerPages(clsid, pDO, hKeyProgID, psh); } RegCloseKey(hKey); diff --git a/dll/win32/shell32/shell32.spec b/dll/win32/shell32/shell32.spec index c10620c236e..c6fb211fe49 100644 --- a/dll/win32/shell32/shell32.spec +++ b/dll/win32/shell32/shell32.spec @@ -466,5 +466,5 @@ 754 stub -noname SHLimitInputEditWithFlags 755 stdcall -noname PathIsEqualOrSubFolder(wstr wstr) 756 stub -noname DeleteFileThumbnail -757 stdcall -version=0x600+ DisplayNameOfW(ptr ptr long ptr long) -866 stdcall -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr) +757 stdcall -noname -version=0x600+ DisplayNameOfW(ptr ptr long ptr long) +866 stdcall -noname -version=0x600+ SHExtCoCreateInstance(wstr ptr ptr ptr ptr) diff --git a/dll/win32/shell32/shfldr.h b/dll/win32/shell32/shfldr.h index a21be88de53..718eaf4ea16 100644 --- a/dll/win32/shell32/shfldr.h +++ b/dll/win32/shell32/shfldr.h @@ -184,6 +184,7 @@ void CloseRegKeyArray(HKEY* array, UINT cKeys); LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys); LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys); void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys); +void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys); #ifdef __cplusplus diff --git a/dll/win32/shell32/shlfolder.cpp b/dll/win32/shell32/shlfolder.cpp index d425dc9b70b..b2f9c0a219c 100644 --- a/dll/win32/shell32/shlfolder.cpp +++ b/dll/win32/shell32/shlfolder.cpp @@ -490,12 +490,23 @@ void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, AddClassKeyToArray(L"AllFilesystemObjects", array, cKeys); AddClassKeyToArray(L"Directory", array, cKeys); } + else if (_ILIsDrive(pidl)) + { + AddClassKeyToArray(L"Drive", array, cKeys); + AddClassKeyToArray(L"Folder", array, cKeys); + } else { ERR("Got non FS pidl\n"); } } +void AddPidlClassKeysToArray(LPCITEMIDLIST pidl, HKEY* array, UINT* cKeys) +{ + if ((pidl = ILFindLastID(pidl)) != NULL) + AddFSClassKeysToArray(1, &pidl, array, cKeys); +} + HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl) { CDataObjectHIDA cida(pDataObject); diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 8f9e000b08b..5a64f514094 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -1836,6 +1836,35 @@ SHELL_CreateShell32DefaultExtractIcon(int IconIndex, REFIID riid, LPVOID *ppvOut return initIcon->QueryInterface(riid, ppvOut); } +int DCIA_AddEntry(HDCIA hDCIA, REFCLSID rClsId) +{ + for (UINT i = 0;; ++i) + { + const CLSID *pClsId = DCIA_GetEntry(hDCIA, i); + if (!pClsId) + break; + if (IsEqualGUID(*pClsId, rClsId)) + return i; // Don't allow duplicates + } + return DSA_AppendItem((HDSA)hDCIA, const_cast(&rClsId)); +} + +void DCIA_AddShellExSubkey(HDCIA hDCIA, HKEY hProgId, PCWSTR pszSubkey) +{ + WCHAR szKey[200]; + PathCombineW(szKey, L"shellex", pszSubkey); + HKEY hEnum; + if (RegOpenKeyExW(hProgId, szKey, 0, KEY_READ, &hEnum) != ERROR_SUCCESS) + return; + for (UINT i = 0; RegEnumKeyW(hEnum, i++, szKey, _countof(szKey)) == ERROR_SUCCESS;) + { + CLSID clsid; + if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hEnum, szKey, &clsid))) + DCIA_AddEntry(hDCIA, clsid); + } + RegCloseKey(hEnum); +} + /************************************************************************* * SHIsBadInterfacePtr [SHELL32.84] * diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h index 2c4e4dc614a..3ac7363416a 100644 --- a/dll/win32/shell32/utils.h +++ b/dll/win32/shell32/utils.h @@ -120,6 +120,14 @@ SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut) return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut); } +typedef HDSA HDCIA; // DynamicClassIdArray +#define DCIA_Create() ( (HDCIA)DSA_Create(sizeof(CLSID), 4) ) +#define DCIA_Destroy(hDCIA) DSA_Destroy((HDSA)(hDCIA)) +#define DCIA_GetCount(hDCIA) DSA_GetItemCount((HDSA)(hDCIA)) +#define DCIA_GetEntry(hDCIA, iItem) ( (const CLSID*)DSA_GetItemPtr((HDSA)(hDCIA), (iItem)) ) +int DCIA_AddEntry(HDCIA hDCIA, REFCLSID rClsId); +void DCIA_AddShellExSubkey(HDCIA hDCIA, HKEY hProgId, PCWSTR pszSubkey); + #ifdef __cplusplus struct ClipboardViewerChain { diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index 0cd360feba8..98bfbd36ab9 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -133,6 +133,11 @@ HGLOBAL RenderSHELLIDLIST (LPITEMIDLIST pidlRoot, LPITEMIDLIST * apidl, UINT cid HGLOBAL RenderFILENAMEA (LPITEMIDLIST pidlRoot, LPITEMIDLIST * apidl, UINT cidl) DECLSPEC_HIDDEN; HGLOBAL RenderFILENAMEW (LPITEMIDLIST pidlRoot, LPITEMIDLIST * apidl, UINT cidl) DECLSPEC_HIDDEN; +HRESULT SHELL_GetShellExtensionRegCLSID( + HKEY hKey, + LPCWSTR KeyName, + CLSID *pClsId); + /* Change Notification */ void InitChangeNotifications(void) DECLSPEC_HIDDEN; void FreeChangeNotifications(void) DECLSPEC_HIDDEN;