From 16bb138e32536ccc818968bec2f06a576f0d2c93 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Tue, 4 Aug 2026 18:39:14 +0200 Subject: [PATCH] [SHELL32][SHELL32_APITEST] Handle named registry verbs in CDefaultContextMenu::InvokeCommand (#9340) CORE-20690 --- dll/win32/shell32/CCopyMoveToMenu.cpp | 88 +++++++++++------ dll/win32/shell32/CCopyMoveToMenu.h | 2 + dll/win32/shell32/CDefView.cpp | 22 +---- dll/win32/shell32/CDefaultContextMenu.cpp | 97 ++++++------------- dll/win32/shell32/propsheet.cpp | 4 +- dll/win32/shell32/shlexec.cpp | 31 +++--- dll/win32/shell32/wine/shell32_main.h | 9 +- .../rostests/apitests/shell32/CMakeLists.txt | 2 +- .../apitests/shell32/ShellExecuteEx.cpp | 60 ++++++++++++ modules/rostests/apitests/shell32/testlist.c | 2 +- 10 files changed, 180 insertions(+), 137 deletions(-) diff --git a/dll/win32/shell32/CCopyMoveToMenu.cpp b/dll/win32/shell32/CCopyMoveToMenu.cpp index cfc044e8514..2bfe912b690 100644 --- a/dll/win32/shell32/CCopyMoveToMenu.cpp +++ b/dll/win32/shell32/CCopyMoveToMenu.cpp @@ -17,6 +17,19 @@ CCopyMoveToMenu::CCopyMoveToMenu() : { } +static BOOL +IsValidTarget(CCopyMoveToMenu *pThis, LPCITEMIDLIST pidl) +{ + WCHAR szPath[MAX_PATH]; + + if (ILIsEqual(pidl, pThis->m_pidlFolder)) + return pThis->GetFileOp() == FO_COPY; + + szPath[0] = UNICODE_NULL; + SHGetPathFromIDListW(pidl, szPath); + return _ILIsDesktop(pidl) || PathFileExistsW(szPath); +} + static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -62,20 +75,12 @@ WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) static INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) { - CCopyMoveToMenu *this_ = - reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + CCopyMoveToMenu *this_ = reinterpret_cast(lpData); switch (uMsg) { case BFFM_INITIALIZED: { - SetWindowLongPtr(hwnd, GWLP_USERDATA, lpData); - this_ = reinterpret_cast(lpData); - - // Select initial directory - SendMessageW(hwnd, BFFM_SETSELECTION, FALSE, - reinterpret_cast(static_cast(this_->m_pidlFolder))); - // Set caption CString strCaption(MAKEINTRESOURCEW(this_->GetCaptionStringID())); SetWindowTextW(hwnd, strCaption); @@ -85,31 +90,30 @@ BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) SetDlgItemText(hwnd, IDOK, strCopyOrMove); // Subclassing + // TODO: Replace this with BIF_VALIDATE + SetWindowLongPtr(hwnd, GWLP_USERDATA, lpData); this_->m_fnOldWndProc = reinterpret_cast( SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast(WindowProc))); - // Disable OK - PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE); + // Expand "My Computer" + CComHeapPtr drivesPidl(SHCloneSpecialIDList(hwnd, CSIDL_DRIVES, TRUE)); + if (drivesPidl) + SendMessageW(hwnd, BFFM_SETEXPANDED, FALSE, (LPARAM)(LPITEMIDLIST)drivesPidl); + + // Select initial directory + // TODO: Remember the last directory used by the user + CComHeapPtr pidl(SHCloneSpecialIDList(hwnd, CSIDL_PERSONAL, TRUE)); + if (pidl) + SendMessageW(hwnd, BFFM_SETSELECTION, FALSE, (LPARAM)(LPITEMIDLIST)pidl); + + SendMessageW(hwnd, BFFM_ENABLEOK, 0, pidl && IsValidTarget(this_, pidl)); break; } case BFFM_SELCHANGED: { - if (!this_) - break; - - WCHAR szPath[MAX_PATH]; LPCITEMIDLIST pidl = reinterpret_cast(lParam); - - szPath[0] = 0; - SHGetPathFromIDListW(pidl, szPath); - - if (ILIsEqual(pidl, this_->m_pidlFolder)) - PostMessageW(hwnd, BFFM_ENABLEOK, 0, this_->GetFileOp() == FO_COPY); - else if (PathFileExistsW(szPath) || _ILIsDesktop(pidl)) - PostMessageW(hwnd, BFFM_ENABLEOK, 0, TRUE); - else - PostMessageW(hwnd, BFFM_ENABLEOK, 0, FALSE); + PostMessageW(hwnd, BFFM_ENABLEOK, 0, IsValidTarget(this_, pidl)); // the text box will be updated later soon, ignore it this_->m_bIgnoreTextBoxChange = TRUE; @@ -123,6 +127,7 @@ BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) HRESULT CCopyMoveToMenu::DoRealFileOp(const CIDA *pCIDA, LPCMINVOKECOMMANDINFO lpici, PCUIDLIST_ABSOLUTE pidlDestination) { + // TODO: Use SH32_SimulateDropWithSite instead CStringW strFiles; WCHAR szPath[MAX_PATH]; for (UINT n = 0; n < pCIDA->cidl; ++n) @@ -340,15 +345,13 @@ CCopyMoveToMenu::GetCommandString( LPSTR pszName, UINT cchMax) { +#if 1 // Windows does not implement any of these but we will continue to do so as long as we (incorrectly) show them in the context menu if ((uType | GCS_UNICODE) == GCS_VALIDATEW) return idCmd == IDC_ACTION ? S_OK : S_FALSE; if (uType == GCS_VERBW && idCmd == IDC_ACTION) return SHAnsiToUnicode(GetVerb(), (LPWSTR)pszName, cchMax); - - FIXME("%p %lu %u %p %p %u\n", this, - idCmd, uType, pwReserved, pszName, cchMax); - +#endif return E_NOTIMPL; } @@ -398,3 +401,30 @@ CCopyMoveToMenu::GetSite(REFIID riid, void **ppvSite) return m_pSite->QueryInterface(riid, ppvSite); } + +HRESULT +CCopyMoveToMenu::DoCopyMoveToFolder(BOOL Copy, HWND hWnd, IUnknown *pSite, + IShellFolder *pSF, UINT cidl, PCUITEMID_CHILD_ARRAY pidls) +{ + SFGAOF attr = SFGAO_CANCOPY | SFGAO_CANMOVE; + HRESULT hr = pSF->GetAttributesOf(cidl, pidls, &attr); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + if (!(attr & (Copy ? SFGAO_CANCOPY : SFGAO_CANMOVE))) + return E_INVALIDARG; + + CComPtr pDO; + hr = pSF->GetUIObjectOf(hWnd, cidl, pidls, IID_NULL_PPV_ARG(IDataObject, &pDO)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + REFCLSID clsid = Copy ? CLSID_CopyToMenu : CLSID_MoveToMenu; + CComPtr pCM; + hr = SHELL_InitializeExtension(clsid, NULL, pDO, NULL, IID_PPV_ARG(IContextMenu, &pCM)); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + IUnknown_SetSite(pCM, pSite); + + CMINVOKECOMMANDINFO ici = { sizeof(ici), 0, hWnd, MAKEINTRESOURCEA(IDC_ACTION), NULL, NULL, SW_SHOW }; + return pCM->InvokeCommand(&ici); +} diff --git a/dll/win32/shell32/CCopyMoveToMenu.h b/dll/win32/shell32/CCopyMoveToMenu.h index 4dbb214fed0..9479fcb1022 100644 --- a/dll/win32/shell32/CCopyMoveToMenu.h +++ b/dll/win32/shell32/CCopyMoveToMenu.h @@ -46,6 +46,8 @@ public: // IObjectWithSite STDMETHODIMP SetSite(IUnknown *pUnkSite) override; STDMETHODIMP GetSite(REFIID riid, void **ppvSite) override; + + static HRESULT DoCopyMoveToFolder(BOOL Copy, HWND hWnd, IUnknown *pSite, IShellFolder *pSF, UINT cidl, PCUITEMID_CHILD_ARRAY pidls); }; class CCopyToMenu diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index 75cd448e7de..2a10cc20572 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -2487,25 +2487,9 @@ void CDefView::DoActivate(UINT uState) void CDefView::_DoCopyToMoveToFolder(BOOL bCopy) { - if (!GetSelections()) - return; - - SFGAOF rfg = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_FILESYSTEM; - HRESULT hr = m_pSFParent->GetAttributesOf(m_cidl, m_apidl, &rfg); - if (FAILED_UNEXPECTEDLY(hr)) - return; - - if (!bCopy && !(rfg & SFGAO_CANMOVE)) - return; - if (bCopy && !(rfg & SFGAO_CANCOPY)) - return; - - CComPtr pCM; - hr = m_pSFParent->GetUIObjectOf(m_hWnd, m_cidl, m_apidl, IID_IContextMenu, 0, (void **)&pCM); - if (FAILED_UNEXPECTEDLY(hr)) - return; - - InvokeContextMenuCommand(pCM, (bCopy ? "copyto" : "moveto"), NULL); + if (GetSelections()) + CCopyMoveToMenu::DoCopyMoveToFolder(bCopy, m_hWnd, static_cast(this), + m_pSFParent, m_cidl, m_apidl); } LRESULT CDefView::OnActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index 2e0fd193136..e15945c642a 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -164,10 +164,7 @@ static const struct _StaticInvokeCommandMap_ SHORT DfmCmd; } g_StaticInvokeCmdMap[] = { - { "runas", 0 }, // Unimplemented - { "print", 0 }, // Unimplemented - { "preview", 0 }, // Unimplemented - { "open", FCIDM_SHVIEW_OPEN }, + // Note: Verbs from the registry should not be listed here { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER }, { "cut", FCIDM_SHVIEW_CUT, /* ? */ }, { "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY }, @@ -176,8 +173,6 @@ static const struct _StaticInvokeCommandMap_ { "delete", FCIDM_SHVIEW_DELETE, (SHORT)DFM_CMD_DELETE }, { "properties", FCIDM_SHVIEW_PROPERTIES, (SHORT)DFM_CMD_PROPERTIES }, { "rename", FCIDM_SHVIEW_RENAME, (SHORT)DFM_CMD_RENAME }, - { "copyto", FCIDM_SHVIEW_COPYTO }, - { "moveto", FCIDM_SHVIEW_MOVETO }, }; PCSTR MapFcidmCmdToVerb(_In_ UINT_PTR CmdId) @@ -328,7 +323,6 @@ class CDefaultContextMenu : UINT AddShellExtensionsToMenu(HMENU hMenu, UINT* pIndexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags); UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT* IndexMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags); HRESULT DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink); - HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoDelete(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bCopy); @@ -336,7 +330,6 @@ class CDefaultContextMenu : HRESULT DoProperties(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT DoCreateNewFolder(LPCMINVOKECOMMANDINFOEX lpici); - HRESULT DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy); HRESULT InvokeShellExt(LPCMINVOKECOMMANDINFOEX lpcmi); HRESULT InvokeRegVerb(LPCMINVOKECOMMANDINFOEX lpcmi); DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStaticShellEntry pEntry); @@ -345,6 +338,11 @@ class CDefaultContextMenu : PDynamicShellEntry GetDynamicEntry(UINT idCmd); BOOL MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode); + HRESULT GetNoAssocError() + { + return LOBYTE(GetVersion()) < 6 ? E_INVALIDARG : HResultFromWin32(ERROR_NO_ASSOCIATION); + } + public: CDefaultContextMenu(); ~CDefaultContextMenu(); @@ -1108,13 +1106,6 @@ HRESULT CDefaultContextMenu::DoPaste(LPCMINVOKECOMMANDINFOEX lpcmi, BOOL bLink) return S_OK; } -HRESULT -CDefaultContextMenu::DoOpenOrExplore(LPCMINVOKECOMMANDINFOEX lpcmi) -{ - UNIMPLEMENTED; - return E_FAIL; -} - HRESULT CDefaultContextMenu::DoCreateLink(LPCMINVOKECOMMANDINFOEX lpcmi) { HRESULT hr = _DoInvokeCommandCallback(lpcmi, DFM_CMD_LINK); @@ -1239,43 +1230,6 @@ CDefaultContextMenu::DoUndo(LPCMINVOKECOMMANDINFOEX lpcmi) return E_NOTIMPL; } -HRESULT -CDefaultContextMenu::DoCopyToMoveToFolder(LPCMINVOKECOMMANDINFOEX lpici, BOOL bCopy) -{ - HRESULT hr = E_FAIL; - if (!m_pDataObj) - { - ERR("m_pDataObj is NULL\n"); - return hr; - } - - CComPtr pContextMenu; - if (bCopy) - hr = SHCoCreateInstance(NULL, &CLSID_CopyToMenu, NULL, - IID_PPV_ARG(IContextMenu, &pContextMenu)); - else - hr = SHCoCreateInstance(NULL, &CLSID_MoveToMenu, NULL, - IID_PPV_ARG(IContextMenu, &pContextMenu)); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - CComPtr pInit; - hr = pContextMenu->QueryInterface(IID_PPV_ARG(IShellExtInit, &pInit)); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - hr = pInit->Initialize(m_pidlFolder, m_pDataObj, NULL); - if (FAILED_UNEXPECTEDLY(hr)) - return hr; - - if (bCopy) - lpici->lpVerb = "copyto"; - else - lpici->lpVerb = "moveto"; - - return pContextMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)lpici); -} - // This code is taken from CNewMenu and should be shared between the 2 classes HRESULT CDefaultContextMenu::DoCreateNewFolder( @@ -1358,18 +1312,16 @@ BOOL CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) { WCHAR UnicodeStr[MAX_VERB]; + UINT i; /* Loop through all the static verbs looking for a match */ - for (UINT i = 0; i < _countof(g_StaticInvokeCmdMap); i++) + for (i = 0; i < _countof(g_StaticInvokeCmdMap); i++) { - /* We can match both ANSI and unicode strings */ if (IsUnicode) { - /* The static verbs are ANSI, get a unicode version before doing the compare */ SHAnsiToUnicode(g_StaticInvokeCmdMap[i].szStringVerb, UnicodeStr, MAX_VERB); if (!_wcsicmp(UnicodeStr, (LPWSTR)Verb)) { - /* Return the Corresponding Id */ *idCmd = g_StaticInvokeCmdMap[i].IntVerb; return TRUE; } @@ -1384,7 +1336,22 @@ CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) } } - for (POSITION it = m_DynamicEntries.GetHeadPosition(); it != NULL;) + POSITION it; + HRESULT hr = S_OK; + // Check the registry verbs + if (!IsUnicode) + hr = StringCchPrintfW(UnicodeStr, _countof(UnicodeStr), L"%hs", Verb); + for (i = 0, it = m_StaticEntries.GetHeadPosition(); it && SUCCEEDED(hr); ++i) + { + StaticShellEntry& entry = m_StaticEntries.GetNext(it); + if (!_wcsicmp(entry.Verb, UnicodeStr)) + { + *idCmd = m_iIdSCMFirst + i; + return TRUE; + } + } + + for (it = m_DynamicEntries.GetHeadPosition(); it != NULL;) { DynamicShellEntry& entry = m_DynamicEntries.GetNext(it); if (!entry.NumIds) @@ -1396,7 +1363,7 @@ CDefaultContextMenu::MapVerbToCmdId(PVOID Verb, PUINT idCmd, BOOL IsUnicode) return TRUE; } } - return FALSE; + return FALSE; // Note: Even if the verb is "open", we must fail if we can't find a handler } HRESULT @@ -1547,7 +1514,7 @@ CDefaultContextMenu::InvokeRegVerb( POSITION it = m_StaticEntries.FindIndex(iCmd); if (it == NULL) - return E_INVALIDARG; + return HResultFromWin32(ERROR_NO_ASSOCIATION); PStaticShellEntry pEntry = &m_StaticEntries.GetAt(it); @@ -1670,7 +1637,7 @@ CDefaultContextMenu::InvokeCommand( if (MapVerbToCmdId((LPVOID)LocalInvokeInfo.lpVerb, &CmdId, FALSE)) LocalInvokeInfo.lpVerb = MAKEINTRESOURCEA(CmdId); else - return E_INVALIDARG; + return GetNoAssocError(); } CmdId = LOWORD(LocalInvokeInfo.lpVerb); @@ -1719,10 +1686,6 @@ CDefaultContextMenu::InvokeCommand( case FCIDM_SHVIEW_INSERTLINK: Result = DoPaste(&LocalInvokeInfo, TRUE); break; - case FCIDM_SHVIEW_OPEN: - case FCIDM_SHVIEW_EXPLORE: - Result = DoOpenOrExplore(&LocalInvokeInfo); - break; case FCIDM_SHVIEW_COPY: case FCIDM_SHVIEW_CUT: Result = DoCopyOrCut(&LocalInvokeInfo, CmdId == FCIDM_SHVIEW_COPY); @@ -1742,12 +1705,6 @@ CDefaultContextMenu::InvokeCommand( case FCIDM_SHVIEW_NEWFOLDER: Result = DoCreateNewFolder(&LocalInvokeInfo); break; - case FCIDM_SHVIEW_COPYTO: - Result = DoCopyToMoveToFolder(&LocalInvokeInfo, TRUE); - break; - case FCIDM_SHVIEW_MOVETO: - Result = DoCopyToMoveToFolder(&LocalInvokeInfo, FALSE); - break; case FCIDM_SHVIEW_UNDO: Result = DoUndo(&LocalInvokeInfo); break; diff --git a/dll/win32/shell32/propsheet.cpp b/dll/win32/shell32/propsheet.cpp index c90ee679b01..e0d530c7958 100644 --- a/dll/win32/shell32/propsheet.cpp +++ b/dll/win32/shell32/propsheet.cpp @@ -9,7 +9,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); -HRESULT +EXTERN_C HRESULT SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId) { // First try the key name @@ -22,7 +22,7 @@ SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId) return !err ? SHCLSIDFromStringW(buf, pClsId) : HRESULT_FROM_WIN32(err); } -static HRESULT +EXTERN_C HRESULT SHELL_InitializeExtension(REFCLSID clsid, PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDO, HKEY hkeyProgID, REFIID riid, void **ppv) { *ppv = NULL; diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index 4f484d89998..63d28c37566 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -1831,7 +1831,6 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei) return hr; CComHeapPtr verb, parameters, dir; - __SHCloneStrWtoA(&verb, sei->lpVerb); __SHCloneStrWtoA(¶meters, sei->lpParameters); __SHCloneStrWtoA(&dir, sei->lpDirectory); @@ -1841,6 +1840,7 @@ static HRESULT ShellExecute_ContextMenuVerb(LPSHELLEXECUTEINFOW sei) ici.nShow = sei->nShow; if (!fDefault) { + __SHCloneStrWtoA(&verb, sei->lpVerb); ici.lpVerb = verb; ici.lpVerbW = sei->lpVerb; } @@ -2185,6 +2185,19 @@ static void do_error_dialog(UINT_PTR retval, HWND hwnd, PCWSTR filename) SetLastError(error_code); // Restore } +static UINT_PTR ExecuteFailed(UINT_PTR retval, SHELLEXECUTEINFOW &sei, LPCWSTR lpFile, BOOL AllowOpenWith = TRUE) +{ + if (retval <= 32 && !(sei.fMask & SEE_MASK_FLAG_NO_UI)) + { + if (retval == SE_ERR_NOASSOC && !(sei.fMask & SEE_MASK_CLASSALL) && AllowOpenWith) + retval = InvokeOpenWith(sei.hwnd, sei); + if (retval <= 32) + do_error_dialog(retval, sei.hwnd, lpFile); + } + sei.hInstApp = (HINSTANCE)UlongToHandle(retval > 32 ? 33 : retval); + return retval; +} + static WCHAR *expand_environment( const WCHAR *str ) { CHeapPtr buf; @@ -2351,8 +2364,7 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) } /* process the IDList */ - if (sei_tmp.fMask & SEE_MASK_IDLIST && - (sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) != SEE_MASK_INVOKEIDLIST) + if ((sei_tmp.fMask & SEE_MASK_INVOKEIDLIST) == SEE_MASK_IDLIST) { LPCITEMIDLIST pidl = (LPCITEMIDLIST)sei_tmp.lpIDList; hr = SHGetNameAndFlagsW(pidl, SHGDN_FORPARSING, wszApplicationName, dwApplicationNameLen, NULL); @@ -2385,6 +2397,9 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) sei->hInstApp = (HINSTANCE)42; return TRUE; } + UINT err = HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : GetLastError(); + SetLastError(err ? err : ERROR_ACCESS_DENIED); + return ExecuteFailed(retval, *sei, NULL, FALSE) > 32; } if (ERROR_SUCCESS == ShellExecute_FromContextMenuHandlers(&sei_tmp)) @@ -2588,16 +2603,10 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) TRACE("retval %lu\n", retval); - if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) - { - if (retval == SE_ERR_NOASSOC && !(sei->fMask & SEE_MASK_CLASSALL)) - retval = InvokeOpenWith(sei_tmp.hwnd, *sei); - if (retval <= 32) - do_error_dialog(retval, sei_tmp.hwnd, lpFile); - } + if (retval <= 32) + retval = ExecuteFailed(retval, *sei, lpFile); sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval); - return retval > 32; } diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index 724a78b8336..6058fd25651 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -165,10 +165,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); +EXTERN_C HRESULT +SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId); +EXTERN_C HRESULT +SHELL_InitializeExtension(REFCLSID clsid, PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDO, + HKEY hkeyProgID, REFIID riid, void **ppv); /* Change Notification */ void InitChangeNotifications(void) DECLSPEC_HIDDEN; diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 7f1f6a1c5ea..531776c6c98 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -46,7 +46,7 @@ list(APPEND SOURCE She.cpp #ShellExec_RunDLL.cpp # Broke on Windows #ShellExecCmdLine.cpp # Broke on Windows - #ShellExecuteEx.cpp # Broke on Windows + ShellExecuteEx.cpp #ShellExecuteW.cpp # Broke on Windows ShellHook.cpp ShellInfo.cpp diff --git a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp index 6d6b94e5723..ec104bdda5e 100644 --- a/modules/rostests/apitests/shell32/ShellExecuteEx.cpp +++ b/modules/rostests/apitests/shell32/ShellExecuteEx.cpp @@ -17,6 +17,27 @@ #include #include "shell32_apitest_sub.h" +static void RunTest(LPTHREAD_START_ROUTINE TestFunc, UINT TimeOut = 1000 * 30) +{ + // These tests have a tendency to hang, kill it if it takes too long + HANDLE hThread = CreateThread(NULL, 0, TestFunc, NULL, 0, NULL); + if (hThread) + { + if (WaitForSingleObject(hThread, TimeOut) != WAIT_OBJECT_0) + TerminateThread(hThread, ERROR_OPERATION_ABORTED); + CloseHandle(hThread); + } +} + +template static T& Reset(T &sei, UINT flags = SEE_MASK_FLAG_NO_UI) +{ + ZeroMemory(&sei, sizeof(T)); + sei.cbSize = sizeof(T); + sei.fMask = flags; + sei.nShow = SW_SHOW; + return sei; +} + static WCHAR s_win_dir[MAX_PATH]; static WCHAR s_sys_dir[MAX_PATH]; static WCHAR s_win_notepad[MAX_PATH]; @@ -511,8 +532,47 @@ static void test_DoInvalidDir(void) CloseHandle(sei.hProcess); } +static DWORD CALLBACK Test_InvokeIdList(LPVOID) +{ + CCoInit ComInit; + UINT err, ret; + WCHAR Path[MAX_PATH * 2]; + CComHeapPtr pidl; + SHELLEXECUTEINFOW sei; + + Reset(sei, SEE_MASK_FLAG_NO_UI | SEE_MASK_INVOKEIDLIST).nShow = SW_HIDE; + GetSystemDirectoryW(Path, _countof(Path)); + PathAppendW(Path, L"cmd.exe"); + sei.lpParameters = L"/C exit 0"; + sei.lpIDList = LPITEMIDLIST((pidl.Attach(ILCreateFromPathW(Path)), pidl)); + if (!pidl) + { + skip("No exe to test\n"); + return 0; + } + + sei.lpVerb = NULL; + ok(ShellExecuteExW(&sei), "Failed with error %lu\n", GetLastError()); + + sei.lpVerb = L"open"; + ok(ShellExecuteExW(&sei), "Failed with error %lu\n", GetLastError()); + + sei.lpVerb = L"Shell32_Test_DoesNotExist"; + ret = ShellExecuteExW(&sei), err = GetLastError(); + ok(!ret, "Expected failure\n"); + ok_long(err, IsWindowsVistaOrGreater() ? ERROR_NO_ASSOCIATION : ERROR_INVALID_PARAMETER); + + return 0; +} + START_TEST(ShellExecuteEx) { + RunTest(Test_InvokeIdList); + + // FIXME: These tests are broken (silent skip for now) + if (!lstrcmpiW(L"", L"")) + return; + #ifdef _WIN64 skip("Win64 is not supported yet\n"); return; diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 217b6857af3..e1f34c2dccd 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -106,7 +106,7 @@ const struct test winetest_testlist[] = { "She", func_She }, //{ "ShellExec_RunDLL", func_ShellExec_RunDLL }, Broke on Windows //{ "ShellExecCmdLine", func_ShellExecCmdLine }, Broke on Windows - //{ "ShellExecuteEx", func_ShellExecuteEx }, Broke on Windows + { "ShellExecuteEx", func_ShellExecuteEx }, // Mostly broken on Windows, only simple tests enabled //{ "ShellExecuteW", func_ShellExecuteW }, Broke on Windows { "ShellHook", func_ShellHook }, { "ShellState", func_ShellState },