[SHELL32] Invoke the ItemIDList of shortcuts (#7152)

Invoking the IDList as if the target was double-clicked in Explorer fixes shortcuts to control panel items (both .cpl and ShellFolder types).

CORE-19690
This commit is contained in:
Whindmar Saksit 2024-07-22 19:31:48 +02:00 committed by GitHub
parent a45d375f60
commit f6a25d48d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 52 additions and 24 deletions

View file

@ -110,6 +110,21 @@ static int FindVerbInDefaultVerbList(LPCWSTR List, LPCWSTR Verb)
return -1; return -1;
} }
EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Verb, SIZE_T cchMax)
{
for (UINT i = 0; *List; ++i)
{
while (IsVerbListSeparator(*List))
List++;
LPCWSTR Start = List;
while (*List && !IsVerbListSeparator(*List))
List++;
if (List > Start && i == Index)
return StringCchCopyNW(Verb, cchMax, Start, List - Start);
}
return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
}
class CDefaultContextMenu : class CDefaultContextMenu :
public CComObjectRootEx<CComMultiThreadModelNoCS>, public CComObjectRootEx<CComMultiThreadModelNoCS>,
public IContextMenu3, public IContextMenu3,

View file

@ -2569,7 +2569,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
// as the parent window handle... ? // as the parent window handle... ?
/* FIXME: get using interface set from IObjectWithSite?? */ /* FIXME: get using interface set from IObjectWithSite?? */
// NOTE: We might need an extended version of Resolve that provides us with paths... // NOTE: We might need an extended version of Resolve that provides us with paths...
HRESULT hr = Resolve(lpici->hwnd, 0); HRESULT hr = Resolve(lpici->hwnd, (lpici->fMask & CMIC_MASK_FLAG_NO_UI) ? SLR_NO_UI : 0);
if (FAILED(hr)) if (FAILED(hr))
{ {
TRACE("failed to resolve component error 0x%08x\n", hr); TRACE("failed to resolve component error 0x%08x\n", hr);
@ -2624,7 +2624,7 @@ HRESULT CShellLink::DoOpen(LPCMINVOKECOMMANDINFO lpici)
if (m_pPidl) if (m_pPidl)
{ {
sei.lpIDList = m_pPidl; sei.lpIDList = m_pPidl;
sei.fMask |= SEE_MASK_IDLIST; sei.fMask |= SEE_MASK_INVOKEIDLIST;
} }
else else
{ {
@ -2633,12 +2633,9 @@ HRESULT CShellLink::DoOpen(LPCMINVOKECOMMANDINFO lpici)
sei.lpParameters = args; sei.lpParameters = args;
sei.lpClass = m_sLinkPath; sei.lpClass = m_sLinkPath;
sei.nShow = m_Header.nShowCommand; sei.nShow = m_Header.nShowCommand;
if (lpici->nShow != SW_SHOWNORMAL && lpici->nShow != SW_SHOW)
sei.nShow = lpici->nShow; // Allow invoker to override .lnk show mode
sei.lpDirectory = m_sWorkDir; sei.lpDirectory = m_sWorkDir;
sei.lpVerb = L"open";
// HACK for ShellExecuteExW: Change the default verb if this is a Control Panel applet
if (m_sPath && lstrcmpiW(PathFindExtensionW(m_sPath), L".cpl") == 0)
sei.lpVerb = L"cplopen";
return (ShellExecuteExW(&sei) ? S_OK : E_FAIL); return (ShellExecuteExW(&sei) ? S_OK : E_FAIL);
} }

View file

@ -1557,16 +1557,25 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei)
__SHCloneStrWtoA(&verb, sei->lpVerb); __SHCloneStrWtoA(&verb, sei->lpVerb);
__SHCloneStrWtoA(&parameters, sei->lpParameters); __SHCloneStrWtoA(&parameters, sei->lpParameters);
CMINVOKECOMMANDINFOEX ici = {}; BOOL fDefault = !sei->lpVerb || !sei->lpVerb[0];
ici.cbSize = sizeof ici; CMINVOKECOMMANDINFOEX ici = { sizeof(ici) };
ici.fMask = (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); ici.fMask = (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)) | CMIC_MASK_UNICODE;
ici.nShow = sei->nShow; ici.nShow = sei->nShow;
ici.lpVerb = verb; if (!fDefault)
{
ici.lpVerb = verb;
ici.lpVerbW = sei->lpVerb;
}
ici.hwnd = sei->hwnd; ici.hwnd = sei->hwnd;
ici.lpParameters = parameters; ici.lpParameters = parameters;
ici.lpParametersW = sei->lpParameters;
if ((sei->fMask & (SEE_MASK_HASLINKNAME | SEE_MASK_CLASSNAME)) == SEE_MASK_HASLINKNAME)
{
ici.fMask |= CMIC_MASK_HASLINKNAME;
ici.lpTitleW = sei->lpClass;
}
HMENU hMenu = CreatePopupMenu(); HMENU hMenu = CreatePopupMenu();
BOOL fDefault = !ici.lpVerb || !ici.lpVerb[0];
hr = cm->QueryContextMenu(hMenu, 0, 1, 0x7fff, fDefault ? CMF_DEFAULTONLY : 0); hr = cm->QueryContextMenu(hMenu, 0, 1, 0x7fff, fDefault ? CMF_DEFAULTONLY : 0);
if (!FAILED_UNEXPECTEDLY(hr)) if (!FAILED_UNEXPECTEDLY(hr))
{ {
@ -1575,6 +1584,7 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei)
INT uDefault = GetMenuDefaultItem(hMenu, FALSE, 0); INT uDefault = GetMenuDefaultItem(hMenu, FALSE, 0);
uDefault = (uDefault != -1) ? uDefault - 1 : 0; uDefault = (uDefault != -1) ? uDefault - 1 : 0;
ici.lpVerb = MAKEINTRESOURCEA(uDefault); ici.lpVerb = MAKEINTRESOURCEA(uDefault);
ici.lpVerbW = MAKEINTRESOURCEW(uDefault);
} }
hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);

View file

@ -130,9 +130,11 @@ BOOL HCR_MapTypeToValueA(LPCSTR szExtension, LPSTR szFileType, LONG len, BOOL bP
return TRUE; return TRUE;
} }
EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Verb, SIZE_T cchMax);
BOOL HCR_GetDefaultVerbW( HKEY hkeyClass, LPCWSTR szVerb, LPWSTR szDest, DWORD len ) BOOL HCR_GetDefaultVerbW( HKEY hkeyClass, LPCWSTR szVerb, LPWSTR szDest, DWORD len )
{ {
WCHAR sTemp[MAX_PATH]; WCHAR sTemp[MAX_PATH], verbs[MAX_PATH];
LONG size; LONG size;
HKEY hkey; HKEY hkey;
@ -144,21 +146,25 @@ BOOL HCR_GetDefaultVerbW( HKEY hkeyClass, LPCWSTR szVerb, LPWSTR szDest, DWORD l
return TRUE; return TRUE;
} }
size=len; /* MSDN says to first try the default verb */
*szDest='\0'; size = _countof(verbs);
if (!RegQueryValueW(hkeyClass, L"shell\\", szDest, &size) && *szDest) if (!RegQueryValueW(hkeyClass, L"shell", verbs, &size) && *verbs)
{ {
/* The MSDN says to first try the default verb */ for (UINT i = 0;; ++i)
lstrcpyW(sTemp, L"shell\\");
lstrcatW(sTemp, szDest);
lstrcatW(sTemp, L"\\command");
if (!RegOpenKeyExW(hkeyClass, sTemp, 0, KEY_READ, &hkey))
{ {
RegCloseKey(hkey); if (FAILED(SHELL32_EnumDefaultVerbList(verbs, i, szDest, len)))
TRACE("default verb=%s\n", debugstr_w(szDest)); break;
return TRUE; if (FAILED(StringCchPrintfW(sTemp, _countof(sTemp), L"shell\\%s\\command", szDest)))
break;
if (!RegOpenKeyExW(hkeyClass, sTemp, 0, KEY_READ, &hkey))
{
RegCloseKey(hkey);
TRACE("default verb=%s\n", debugstr_w(szDest));
return TRUE;
}
} }
} }
*szDest = UNICODE_NULL;
/* then fallback to 'open' */ /* then fallback to 'open' */
lstrcpyW(sTemp, L"shell\\open\\command"); lstrcpyW(sTemp, L"shell\\open\\command");