From f6f5490ab41a4446a6026c81bea4cc8255584184 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Thu, 3 Feb 2022 21:54:12 +0100 Subject: [PATCH] [SHELL32] ShellExecute: Implement SEE_MASK_INVOKEIDLIST CORE-18035 Also simplify some error handling by using smart pointers --- dll/win32/shell32/folders/CFSFolder.cpp | 4 +- dll/win32/shell32/shlexec.cpp | 200 ++++++++++++++++-------- 2 files changed, 141 insertions(+), 63 deletions(-) diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index 72325d3973b..c332d812d3a 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -1812,8 +1812,8 @@ HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObjec PUITEMID_CHILD pidlChild = ILClone(ILFindLastID(m_pidlRoot)); LPITEMIDLIST pidlParent = ILClone(m_pidlRoot); ILRemoveLastID(pidlParent); - HRESULT hr = SH_ShowPropertiesDialog(m_sPathTarget, pidlParent, &pidlChild); - if (FAILED(hr)) + BOOL bSuccess = SH_ShowPropertiesDialog(m_sPathTarget, pidlParent, &pidlChild); + if (!bSuccess) ERR("SH_ShowPropertiesDialog failed\n"); ILFree(pidlChild); ILFree(pidlParent); diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index 955d2af840e..7cfc0021470 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -1327,16 +1327,15 @@ static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei) return hkey; } -static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) +static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr& dataObj) { - LPCITEMIDLIST pidllast = NULL; - CComPtr dataobj; - CComPtr shf; + CComHeapPtr allocatedPidl; LPITEMIDLIST pidl = NULL; - HRESULT r; if (sei->fMask & SEE_MASK_CLASSALL) + { pidl = (LPITEMIDLIST)sei->lpIDList; + } else { WCHAR fullpath[MAX_PATH]; @@ -1345,21 +1344,19 @@ static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei ) fullpath[0] = 0; ret = GetFullPathNameW(sei->lpFile, MAX_PATH, fullpath, NULL); if (!ret) - goto end; + return HRESULT_FROM_WIN32(GetLastError()); pidl = ILCreateFromPathW(fullpath); + allocatedPidl.Attach(pidl); } - r = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); - if (FAILED(r)) - goto end; + CComPtr shf; + LPCITEMIDLIST pidllast = NULL; + HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; - shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataobj)); - -end: - if (pidl != sei->lpIDList) - ILFree(pidl); - return dataobj.Detach(); + return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataObj)); } static HRESULT shellex_run_context_menu_default(IShellExtInit *obj, @@ -1431,59 +1428,127 @@ end: return r; } +namespace +{ + struct CCoInit + { + CCoInit() { hres = CoInitialize(NULL); } + ~CCoInit() { if (SUCCEEDED(hres)) { CoUninitialize(); } } + HRESULT hres; + }; +} + static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei) { - // Can not use CComPtr here because of CoUninitialize at the end, before the destructors would run. - IDataObject *dataobj = NULL; - IObjectWithSite *ows = NULL; - IShellExtInit *obj = NULL; - HRESULT r; - TRACE("%p %s %p\n", hkey, debugstr_guid(guid), sei); - r = CoInitialize(NULL); - if (FAILED(r)) - goto end; + CCoInit coInit; - r = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, + if (FAILED_UNEXPECTEDLY(coInit.hres)) + return coInit.hres; + + CComPtr obj; + HRESULT hr = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellExtInit, &obj)); - if (FAILED(r)) - { - ERR("failed %08x\n", r); - goto end; - } + if (FAILED_UNEXPECTEDLY(hr)) + return hr; - dataobj = shellex_get_dataobj(sei); - if (!dataobj) - { - ERR("failed to get data object\n"); - r = E_FAIL; - goto end; - } + CComPtr dataobj; + hr = shellex_get_dataobj(sei, dataobj); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; - r = obj->Initialize(NULL, dataobj, hkey); - if (FAILED(r)) - goto end; + hr = obj->Initialize(NULL, dataobj, hkey); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; - r = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); - if (FAILED(r)) - goto end; + CComPtr ows; + hr = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; ows->SetSite(NULL); - r = shellex_run_context_menu_default(obj, sei); - -end: - if (ows) - ows->Release(); - if (dataobj) - dataobj->Release(); - if (obj) - obj->Release(); - CoUninitialize(); - return r; + return shellex_run_context_menu_default(obj, sei); } +static HRESULT shellex_get_contextmenu(LPSHELLEXECUTEINFOW sei, CComPtr& cm) +{ + CComHeapPtr allocatedPidl; + LPITEMIDLIST pidl = NULL; + + if (sei->lpIDList) + { + pidl = (LPITEMIDLIST)sei->lpIDList; + } + else + { + SFGAOF sfga = 0; + HRESULT hr = SHParseDisplayName(sei->lpFile, NULL, &allocatedPidl, SFGAO_STORAGECAPMASK, &sfga); + if (FAILED(hr)) + return hr; + + pidl = allocatedPidl; + } + + CComPtr shf; + LPCITEMIDLIST pidllast = NULL; + HRESULT hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast); + if (FAILED(hr)) + return hr; + + return shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IContextMenu, &cm)); +} + +static HRESULT IContextMenu_exec(LPSHELLEXECUTEINFOW sei) +{ + TRACE("%p\n", sei); + + CCoInit coInit; + + if (FAILED_UNEXPECTEDLY(coInit.hres)) + return coInit.hres; + + CComPtr cm; + HRESULT hr = shellex_get_contextmenu(sei, cm); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + CComHeapPtr verb, parameters; + __SHCloneStrWtoA(&verb, sei->lpVerb); + __SHCloneStrWtoA(¶meters, sei->lpParameters); + + CMINVOKECOMMANDINFOEX ici = {}; + ici.cbSize = sizeof ici; + ici.fMask = (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI)); + ici.nShow = sei->nShow; + ici.lpVerb = verb; + ici.hwnd = sei->hwnd; + ici.lpParameters = parameters; + + HMENU hMenu = CreatePopupMenu(); + BOOL fDefault = !ici.lpVerb || !ici.lpVerb[0]; + hr = cm->QueryContextMenu(hMenu, 0, 1, 0x7fff, fDefault ? CMF_DEFAULTONLY : 0); + if (!FAILED_UNEXPECTEDLY(hr)) + { + if (fDefault) + { + INT uDefault = GetMenuDefaultItem(hMenu, FALSE, 0); + uDefault = (uDefault != -1) ? uDefault - 1 : 0; + ici.lpVerb = MAKEINTRESOURCEA(uDefault); + } + + hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici); + if (!FAILED_UNEXPECTEDLY(hr)) + hr = S_OK; + } + + DestroyMenu(hMenu); + + return hr; +} + + /************************************************************************* * ShellExecute_FromContextMenu [Internal] @@ -1751,7 +1816,7 @@ static WCHAR *expand_environment( const WCHAR *str ) static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) { static const DWORD unsupportedFlags = - SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY | + SEE_MASK_ICON | SEE_MASK_HOTKEY | SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT | SEE_MASK_UNICODE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR; @@ -1853,15 +1918,12 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) if (sei_tmp.fMask & unsupportedFlags) { - // SEE_MASK_IDLIST is not in unsupportedFlags, but the check above passes because SEE_MASK_INVOKEIDLIST is in it - if ((sei_tmp.fMask & unsupportedFlags) != SEE_MASK_IDLIST) - { - FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags); - } + FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags); } /* process the IDList */ - if (sei_tmp.fMask & SEE_MASK_IDLIST) + if (sei_tmp.fMask & SEE_MASK_IDLIST && + (sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) != SEE_MASK_INVOKEIDLIST) { CComPtr pSEH; @@ -1908,6 +1970,22 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) sei_tmp.lpDirectory = wszDir = tmp; } + if ((sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_INVOKEIDLIST) + { + HRESULT hr = IContextMenu_exec(&sei_tmp); + if (SUCCEEDED(hr)) + { + sei->hInstApp = (HINSTANCE)42; + HeapFree(GetProcessHeap(), 0, wszApplicationName); + if (wszParameters != parametersBuffer) + HeapFree(GetProcessHeap(), 0, wszParameters); + if (wszDir != dirBuffer) + HeapFree(GetProcessHeap(), 0, wszDir); + return TRUE; + } + } + + if (ERROR_SUCCESS == ShellExecute_FromContextMenu(&sei_tmp)) { sei->hInstApp = (HINSTANCE) 33;