From d663eb446689525b376f0fd447fd6eb5f2da3698 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Fri, 30 May 2025 18:46:24 +0200 Subject: [PATCH] [SHELL32][CMD][SHLWAPI] Use the openas verb when invoking unknown file types (#7981) CORE-20184 --- base/shell/cmd/cmd.c | 38 +++---- base/shell/cmd/cmd.h | 1 + base/shell/cmd/error.c | 7 ++ boot/bootdata/hivecls.inf | 10 +- dll/win32/shell32/CDefaultContextMenu.cpp | 128 +++++++++++----------- dll/win32/shell32/COpenWithMenu.cpp | 91 ++++++++++++--- dll/win32/shell32/CQueryAssociations.cpp | 4 +- dll/win32/shell32/folders/CFSFolder.cpp | 1 + dll/win32/shell32/shlexec.cpp | 114 ++++++++----------- dll/win32/shell32/utils.h | 14 +++ dll/win32/shlwapi/assoc.c | 11 +- 11 files changed, 249 insertions(+), 170 deletions(-) diff --git a/base/shell/cmd/cmd.c b/base/shell/cmd/cmd.c index afb67cb7421..d6008188b74 100644 --- a/base/shell/cmd/cmd.c +++ b/base/shell/cmd/cmd.c @@ -266,10 +266,10 @@ typedef BOOL (WINAPI *MYEX)(LPSHELLEXECUTEINFO lpExecInfo); HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params, LPTSTR directory, INT show) { - SHELLEXECUTEINFO sei; + SHELLEXECUTEINFO sei = { sizeof(sei), flags | SEE_MASK_FLAG_DDEWAIT }; HMODULE hShell32; MYEX hShExt; - BOOL ret; + UINT err; TRACE ("RunFile(%s)\n", debugstr_aw(filename)); hShell32 = LoadLibrary(_T("SHELL32.DLL")); @@ -289,19 +289,17 @@ HANDLE RunFile(DWORD flags, LPTSTR filename, LPTSTR params, TRACE ("RunFile: ShellExecuteExA/W is at %x\n", hShExt); - memset(&sei, 0, sizeof sei); - sei.cbSize = sizeof sei; - sei.fMask = flags; sei.lpFile = filename; sei.lpParameters = params; sei.lpDirectory = directory; sei.nShow = show; - ret = hShExt(&sei); - - TRACE ("RunFile: ShellExecuteExA/W returned 0x%p\n", ret); + err = hShExt(&sei) ? ERROR_SUCCESS : GetLastError(); + TRACE ("RunFile: ShellExecuteExA/W returned error %#x\n", err); FreeLibrary(hShell32); - return ret ? sei.hProcess : NULL; + + SetLastError(err); + return err ? NULL : sei.hProcess; } @@ -431,6 +429,7 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd) /* exec the program */ PROCESS_INFORMATION prci; STARTUPINFO stui; + UINT execerror = ERROR_FILE_NOT_FOUND; /* build command line for CreateProcess(): FullName + " " + rest */ BOOL quoted = !!_tcschr(First, _T(' ')); @@ -450,8 +449,8 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd) memset(&stui, 0, sizeof(stui)); stui.cb = sizeof(stui); stui.lpTitle = Full; - stui.dwFlags = STARTF_USESHOWWINDOW; - stui.wShowWindow = SW_SHOWDEFAULT; + stui.dwFlags = 0; + stui.wShowWindow = SW_SHOWNORMAL; /* Set the console to standard mode */ SetConsoleMode(ConStreamGetOSHandle(StdIn), @@ -470,14 +469,12 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd) { CloseHandle(prci.hThread); } - else + else if (GetLastError() == ERROR_BAD_EXE_FORMAT) { // See if we can run this with ShellExecute() ie myfile.xls - prci.hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE, - szFullName, - rest, - NULL, - SW_SHOWNORMAL); + HANDLE hProcess = RunFile(SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE, + szFullName, rest, NULL, SW_SHOWNORMAL); + execerror = hProcess ? ERROR_SUCCESS : GetLastError(); } *FirstEnd = _T('\0'); @@ -499,10 +496,13 @@ Execute(LPTSTR Full, LPTSTR First, LPTSTR Rest, PARSED_COMMAND *Cmd) } CloseHandle(prci.hProcess); } - else + else if (execerror) { TRACE ("[ShellExecute failed!: %s]\n", debugstr_aw(Full)); - error_bad_command(first); + if (execerror == ERROR_NO_ASSOCIATION) + error_cant_exec_program(); + else + error_bad_command(first); dwExitCode = 1; } diff --git a/base/shell/cmd/cmd.h b/base/shell/cmd/cmd.h index 8b486b05619..d8760bf13a0 100644 --- a/base/shell/cmd/cmd.h +++ b/base/shell/cmd/cmd.h @@ -216,6 +216,7 @@ VOID error_parameter_format(TCHAR ch); VOID error_invalid_switch(TCHAR ch); VOID error_invalid_parameter_format(PCTSTR s); VOID error_out_of_memory(VOID); +VOID error_cant_exec_program(VOID); VOID error_syntax(PCTSTR s); VOID msg_pause(VOID); diff --git a/base/shell/cmd/error.c b/base/shell/cmd/error.c index bd39ff56344..e2cec983055 100644 --- a/base/shell/cmd/error.c +++ b/base/shell/cmd/error.c @@ -141,6 +141,13 @@ VOID error_out_of_memory(VOID) nErrorLevel = 1; } +VOID error_cant_exec_program(VOID) +{ + /* TODO: Windows uses the custom string "The system cannot execute the specified program" here */ + ErrorMessage(ERROR_NO_ASSOCIATION, NULL); + nErrorLevel = 1; +} + VOID error_invalid_parameter_format(PCWSTR s) { diff --git a/boot/bootdata/hivecls.inf b/boot/bootdata/hivecls.inf index 55fa8985658..32008e57291 100644 --- a/boot/bootdata/hivecls.inf +++ b/boot/bootdata/hivecls.inf @@ -5,8 +5,8 @@ Signature = "$Windows NT$" HKLM,"SOFTWARE\Classes",,0x00000010 ; Default key -HKCR,"*","",0x00000000,"" -HKCR,"SystemFileAssociations","",0x00000000,"" +HKCR,"*","AlwaysShowExt",0x00000000,"" +HKCR,"SystemFileAssociations",,0x00000010 ; Open With List HKCR,"*\OpenWithList\excel.exe","",0x00000000,"" @@ -16,6 +16,12 @@ HKCR,"*\OpenWithList\notepad.exe","",0x00000000,"" HKCR,"*\OpenWithList\winword.exe","",0x00000000,"" HKCR,"*\OpenWithList\wordpad.exe","",0x00000000,"" +; Unknown +HKCR,"Unknown","AlwaysShowExt" +HKCR,"Unknown","QueryClassStore" +HKCR,"Unknown\shell","",,"openas" +HKCR,"Unknown\shell\openas\command","",0x00020000,"%SystemRoot%\system32\rundll32.exe %SystemRoot%\system32\shell32.dll,OpenAs_RunDLL %1" + ; Folders HKCR,"Folder","",0x00000000,"Folder" ;HKCR,"Folder\DefaultIcon","",0x00000000,"%SystemRoot%\system32\shell32.dll,-4" diff --git a/dll/win32/shell32/CDefaultContextMenu.cpp b/dll/win32/shell32/CDefaultContextMenu.cpp index f631f180e78..6f9a8aef4f7 100644 --- a/dll/win32/shell32/CDefaultContextMenu.cpp +++ b/dll/win32/shell32/CDefaultContextMenu.cpp @@ -58,10 +58,10 @@ static const struct _StaticInvokeCommandMap_ SHORT DfmCmd; } g_StaticInvokeCmdMap[] = { - { "RunAs", 0 }, // Unimplemented - { "Print", 0 }, // Unimplemented - { "Preview", 0 }, // Unimplemented - { "Open", FCIDM_SHVIEW_OPEN }, + { "runas", 0 }, // Unimplemented + { "print", 0 }, // Unimplemented + { "preview", 0 }, // Unimplemented + { "open", FCIDM_SHVIEW_OPEN }, { CMDSTR_NEWFOLDERA, FCIDM_SHVIEW_NEWFOLDER, (SHORT)DFM_CMD_NEWFOLDER }, { "cut", FCIDM_SHVIEW_CUT, /* ? */ }, { "copy", FCIDM_SHVIEW_COPY, (SHORT)DFM_CMD_COPY }, @@ -139,6 +139,36 @@ EXTERN_C HRESULT SHELL32_EnumDefaultVerbList(LPCWSTR List, UINT Index, LPWSTR Ve return HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); } +static HRESULT GetFriendlyVerb(_In_ PCWSTR pszVerb, _Out_ PWSTR pszBuf, _In_ SIZE_T cchMax) +{ + static const struct { PCWSTR pszVerb; WORD iResId; } map[] = + { + // { L"open", IDS_OPEN_VERB }, These two have already been handled + // { L"explore", IDS_EXPLORE_VERB }, + { L"edit", IDS_EDIT_VERB }, + { L"print", IDS_PRINT_VERB }, + { L"runas", IDS_RUNAS_VERB }, + { L"openas", IDS_OPEN_VERB }, + { L"find", IDS_FIND_VERB }, + }; + for (SIZE_T i = 0; i < _countof(map); ++i) + { + if (!_wcsicmp(pszVerb, map[i].pszVerb) && + LoadStringW(shell32_hInstance, map[i].iResId, pszBuf, cchMax)) + { + return S_OK; + } + } + + // Try to make a friendly verb based on the verb subkey + if (pszVerb[0] < 127 && !StrChrW(pszVerb, '&') && SUCCEEDED(StringCchCopyW(pszBuf + 1, --cchMax, pszVerb))) + { + *pszBuf = L'&'; + return S_OK; // This can be changed to S_FALSE if the caller needs to know we faked it + } + return E_FAIL; +} + class CDefaultContextMenu : public CComObjectRootEx, public IContextMenu3, @@ -572,9 +602,8 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( { UINT ntver = RosGetProcessEffectiveVersion(); MENUITEMINFOW mii = { sizeof(mii) }; - UINT idResource; WCHAR wszDispVerb[80]; // The limit on XP. If the friendly string is longer, it falls back to the verb key. - UINT fState; + UINT fState, idVerbRes; UINT cIds = 0, indexFirst = *pIndexMenu, indexDefault; int iDefVerbIndex = -1; @@ -587,6 +616,7 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( { StaticShellEntry& info = m_StaticEntries.GetNext(it); BOOL forceFirstPos = FALSE; + bool hide = false; fState = MFS_ENABLED; @@ -599,86 +629,59 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( if (info.Verb.CompareNoCase(L"open") == 0) { - idResource = IDS_OPEN_VERB; + idVerbRes = IDS_OPEN_VERB; // TODO: This string should include '&' fState |= MFS_DEFAULT; /* override default when open verb is found */ forceFirstPos++; } else if (info.Verb.CompareNoCase(L"explore") == 0) { - idResource = IDS_EXPLORE_VERB; + idVerbRes = IDS_EXPLORE_VERB; // TODO: This string should include '&' if (uFlags & CMF_EXPLORE) { fState |= MFS_DEFAULT; forceFirstPos++; } } - else if (info.Verb.CompareNoCase(L"runas") == 0) - idResource = IDS_RUNAS_VERB; - else if (info.Verb.CompareNoCase(L"edit") == 0) - idResource = IDS_EDIT_VERB; - else if (info.Verb.CompareNoCase(L"find") == 0) - idResource = IDS_FIND_VERB; - else if (info.Verb.CompareNoCase(L"print") == 0) - idResource = IDS_PRINT_VERB; else if (info.Verb.CompareNoCase(L"printto") == 0) - continue; + hide = true; else - idResource = 0; + idVerbRes = 0; /* By default use verb for menu item name */ mii.dwTypeData = (LPWSTR)info.Verb.GetString(); WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; - HRESULT hr; - hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString()); + HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", info.Verb.GetString()); if (FAILED_UNEXPECTEDLY(hr)) - { - continue; - } + hide = true; UINT cmdFlags = 0; - bool hide = false; HKEY hkVerb; - if (idResource > 0) + if (RegOpenKeyExW(info.hkClass, wszKey, 0, KEY_READ, &hkVerb) == ERROR_SUCCESS) { if (!(uFlags & CMF_OPTIMIZEFORINVOKE)) { - if (LoadStringW(shell32_hInstance, idResource, wszDispVerb, _countof(wszDispVerb))) - mii.dwTypeData = wszDispVerb; /* use translated verb */ - else - ERR("Failed to load string\n"); - } + DWORD cbVerb = sizeof(wszDispVerb); + LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL); + if (res || !*wszDispVerb) + res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL); - if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) != ERROR_SUCCESS) - hkVerb = NULL; + if ((res == ERROR_SUCCESS && *wszDispVerb) || + (idVerbRes && LoadStringW(shell32_hInstance, idVerbRes, wszDispVerb, _countof(wszDispVerb))) || + SUCCEEDED(GetFriendlyVerb(info.Verb, wszDispVerb, _countof(wszDispVerb)))) + { + mii.dwTypeData = wszDispVerb; + } + } } else { - if (RegOpenKeyW(info.hkClass, wszKey, &hkVerb) == ERROR_SUCCESS) - { - if (!(uFlags & CMF_OPTIMIZEFORINVOKE)) - { - DWORD cbVerb = sizeof(wszDispVerb); - LONG res = RegLoadMUIStringW(hkVerb, L"MUIVerb", wszDispVerb, cbVerb, NULL, 0, NULL); - if (res || !*wszDispVerb) - res = RegLoadMUIStringW(hkVerb, NULL, wszDispVerb, cbVerb, NULL, 0, NULL); - - if (res == ERROR_SUCCESS && *wszDispVerb) - { - /* use description for the menu entry */ - mii.dwTypeData = wszDispVerb; - } - } - } - else - { - hkVerb = NULL; - } + hkVerb = NULL; } if (hkVerb) { - if (!(uFlags & CMF_EXTENDEDVERBS)) + if (!hide && !(uFlags & CMF_EXTENDEDVERBS)) hide = RegValueExists(hkVerb, L"Extended"); if (!hide) @@ -687,6 +690,9 @@ CDefaultContextMenu::AddStaticContextMenusToMenu( if (!hide && !(uFlags & CMF_DISABLEDVERBS)) hide = RegValueExists(hkVerb, L"LegacyDisable"); + if (DWORD dwRest = (hide ? 0 : RegGetDword(hkVerb, NULL, L"SuppressionPolicy", 0))) + hide = SHRestricted((RESTRICTIONS)dwRest); + if (RegValueExists(hkVerb, L"NeverDefault")) fState &= ~MFS_DEFAULT; @@ -924,6 +930,11 @@ CDefaultContextMenu::QueryContextMenu( // TODO: DFM_MERGECONTEXTMENU_TOP + // TODO: Remove duplicate verbs. This will be easier when the static items handling + // has been moved to CLSID_ShellFileDefExt so we only have to deal with ShellEx. + // This is a Windows XP+ feature. On an unknown file type, Windows 2000 will + // display both "Open" (openas from Unknown) and "Open with..." (openas from *). + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cIds); } @@ -1278,8 +1289,6 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic LPCWSTR FlagsName; WCHAR wszKey[sizeof("shell\\") + MAX_VERB]; HRESULT hr; - DWORD wFlags; - DWORD cbVerb; if (!m_site) return 0; @@ -1306,14 +1315,7 @@ CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFOEX lpcmi, PStatic hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"shell\\%s", pEntry->Verb.GetString()); if (FAILED_UNEXPECTEDLY(hr)) return 0; - - cbVerb = sizeof(wFlags); - if (RegGetValueW(pEntry->hkClass, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS) - { - return wFlags; - } - - return 0; + return RegGetDword(pEntry->hkClass, wszKey, FlagsName, 0); } HRESULT diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp index 9f646c00019..65b41e5d0a0 100644 --- a/dll/win32/shell32/COpenWithMenu.cpp +++ b/dll/win32/shell32/COpenWithMenu.cpp @@ -81,6 +81,67 @@ HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZ return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); } +static HRESULT SH32_EvaluateValidExecApp(_Inout_ PWSTR pszCmd, _In_ SIZE_T cchMax) +{ + // FIXME: SHEvaluateSystemCommandTemplate is not implemented yet, using a minimal version. + if (!PathGetAppFromCommandLine(pszCmd, pszCmd, cchMax)) + return E_FAIL; + + UINT fPRF = PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK; + WCHAR szCurrDir[MAX_PATH]; + LPCWSTR pszDirsArr[2] = { szCurrDir, NULL }, *ppszDirs = NULL; + if (GetCurrentDirectoryW(_countof(szCurrDir), szCurrDir)) + ppszDirs = pszDirsArr; + if (PathResolveW(pszCmd, ppszDirs, fPRF | (ppszDirs ? PRF_FIRSTDIRDEF : 0))) + return S_OK; + return E_FAIL; +} + +HRESULT SH32_InvokeOpenWith(_In_ PCWSTR pszPath, _In_ LPCMINVOKECOMMANDINFO pici, _Out_ HANDLE *phProcess) +{ + if (!pszPath || !pici) + return HResultFromWin32(ERROR_INVALID_PARAMETER); + + HRESULT hr = HResultFromWin32(ERROR_NO_ASSOCIATION); + SHELLEXECUTEINFOW sei = { sizeof(sei), CmicFlagsToSeeFlags(pici->fMask), pici->hwnd }; + sei.fMask |= SEE_MASK_CLASSKEY | SEE_MASK_NOZONECHECKS; + sei.lpFile = pszPath; + sei.nShow = pici->nShow; + if (phProcess) + { + sei.fMask |= SEE_MASK_NOCLOSEPROCESS; + sei.hProcess = NULL; + } + + if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Unknown", 0, KEY_READ, &sei.hkeyClass)) + { + // Use the internal dialog only if HKCR\Unknown\shell\openas\command exists but is invalid. + WCHAR szCmd[MAX_PATH * 2]; + DWORD cch = _countof(szCmd); + hr = AssocQueryStringByKeyW(ASSOCF_NOTRUNCATE | ASSOCF_NOFIXUPS | + ASSOCF_IGNOREBASECLASS | ASSOCF_INIT_IGNOREUNKNOWN, + ASSOCSTR_COMMAND, sei.hkeyClass, NULL, szCmd, &cch); + if (SUCCEEDED(hr) && FAILED(SH32_EvaluateValidExecApp(szCmd, _countof(szCmd)))) + { + OPENASINFO info = { pszPath, NULL, OAIF_EXEC | OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION }; + hr = SHOpenWithDialog(sei.hwnd, &info); + } + else + { + hr = ShellExecuteExW(&sei) ? S_OK : HResultFromWin32(GetLastError()); + } + RegCloseKey(sei.hkeyClass); + } + else if (!(pici->fMask & CMIC_MASK_FLAG_NO_UI)) + { + SHELL_ErrorBox(sei.hwnd, hr); + } + + if (phProcess) + *phProcess = sei.hProcess; + return hr; +} + class COpenWithList { public: @@ -865,6 +926,7 @@ class COpenWithDialog const OPENASINFO *m_pInfo; COpenWithList *m_pAppList; + UINT m_InFlags; BOOL m_bListAllocated; HWND m_hDialog, m_hTreeView; HTREEITEM m_hRecommend; @@ -1031,13 +1093,22 @@ VOID COpenWithDialog::Init(HWND hwnd) m_hDialog = hwnd; SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)this); + UINT fDisallow = 0; + PCWSTR pszExt = PathFindExtensionW(m_pInfo->pcszFile); + // Don't allow registration for "" nor "." nor ".exe" etc. + if (!pszExt || !pszExt[0] || !pszExt[1] || PathIsExeW(m_pInfo->pcszFile)) + fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION; + if (SHRestricted(REST_NOFILEASSOCIATE)) + fDisallow |= OAIF_ALLOW_REGISTRATION | OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT; + /* Handle register checkbox */ + m_InFlags = m_pInfo->oaifInFlags & ~fDisallow; HWND hRegisterCheckbox = GetDlgItem(hwnd, 14003); - if (!(m_pInfo->oaifInFlags & OAIF_ALLOW_REGISTRATION)) + if (!(m_InFlags & OAIF_ALLOW_REGISTRATION)) EnableWindow(hRegisterCheckbox, FALSE); - if (m_pInfo->oaifInFlags & OAIF_FORCE_REGISTRATION) + if (m_InFlags & OAIF_FORCE_REGISTRATION) SendMessage(hRegisterCheckbox, BM_SETCHECK, BST_CHECKED, 0); - if (m_pInfo->oaifInFlags & OAIF_HIDE_REGISTRATION) + if (m_InFlags & OAIF_HIDE_REGISTRATION) ShowWindow(hRegisterCheckbox, SW_HIDE); if (m_pInfo->pcszFile) @@ -1111,7 +1182,7 @@ VOID COpenWithDialog::Accept() if (pApp) { /* Set programm as default handler */ - if (IsDlgButtonChecked(m_hDialog, 14003) == BST_CHECKED) + if (IsDlgButtonChecked(m_hDialog, 14003) == BST_CHECKED && (m_InFlags & OAIF_REGISTER_EXT)) { m_pAppList->SetDefaultHandler(pApp, m_pInfo->pcszFile); // FIXME: Update DefaultIcon registry @@ -1119,7 +1190,7 @@ VOID COpenWithDialog::Accept() } /* Execute program */ - if (m_pInfo->oaifInFlags & OAIF_EXEC) + if (m_InFlags & OAIF_EXEC) m_pAppList->Execute(pApp, m_pInfo->pcszFile); EndDialog(m_hDialog, 1); @@ -1394,15 +1465,7 @@ COpenWithMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici) if (m_idCmdFirst + LOWORD(lpici->lpVerb) == idChooseApp) { DoChooseApp: - OPENASINFO info; - LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); - - info.pcszFile = m_wszPath; - info.oaifInFlags = OAIF_EXEC; - if (pwszExt[0]) - info.oaifInFlags |= OAIF_REGISTER_EXT | OAIF_ALLOW_REGISTRATION; - info.pcszClass = NULL; - hr = SHOpenWithDialog(lpici->hwnd, &info); + hr = SH32_InvokeOpenWith(m_wszPath, lpici, NULL); } else { diff --git a/dll/win32/shell32/CQueryAssociations.cpp b/dll/win32/shell32/CQueryAssociations.cpp index 14a618b9611..3e2fbb384d4 100644 --- a/dll/win32/shell32/CQueryAssociations.cpp +++ b/dll/win32/shell32/CQueryAssociations.cpp @@ -591,7 +591,7 @@ HRESULT CQueryAssociations::GetValue(HKEY hkey, const WCHAR *name, void **data, DWORD size; LONG ret; - ret = RegQueryValueExW(hkey, name, 0, NULL, NULL, &size); + ret = SHQueryValueExW(hkey, name, 0, NULL, NULL, &size); if (ret != ERROR_SUCCESS) return HRESULT_FROM_WIN32(ret); @@ -602,7 +602,7 @@ HRESULT CQueryAssociations::GetValue(HKEY hkey, const WCHAR *name, void **data, if (!*data) return E_OUTOFMEMORY; - ret = RegQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size); + ret = SHQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size); if (ret != ERROR_SUCCESS) { HeapFree(GetProcessHeap(), 0, *data); diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index c72441771d2..e8cdceb3e9c 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -1480,6 +1480,7 @@ BOOL SHELL_FS_HideExtension(LPCWSTR szPath) } } } + // TODO: else if "AlwaysShowExt" return doHide; } diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index d5c3675eb84..a13d0bf3ca3 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -47,6 +47,19 @@ static BOOL SHELL_InRunDllProcess(VOID) return s_bInDllProcess; } +static UINT_PTR InvokeOpenWith(HWND hWndOwner, SHELLEXECUTEINFOW &sei) +{ + extern HRESULT SH32_InvokeOpenWith(PCWSTR, LPCMINVOKECOMMANDINFO, HANDLE *); + + HANDLE *phProc = (sei.fMask & SEE_MASK_NOCLOSEPROCESS) ? &sei.hProcess : NULL; + UINT fCmic = (sei.fMask & SEE_CMIC_COMMON_BASICFLAGS) | CMIC_MASK_FLAG_NO_UI; + CMINVOKECOMMANDINFO ici = { sizeof(ici), fCmic, hWndOwner }; + ici.nShow = SW_SHOW; + HRESULT hr = SH32_InvokeOpenWith(sei.lpFile, &ici, phProc); + SetLastError(ERROR_NO_ASSOCIATION); + return SUCCEEDED(hr) ? 42 : SE_ERR_NOASSOC; +} + static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum) { bool firstCharQuote = false; @@ -697,14 +710,14 @@ static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classn HKEY hkeyClass; WCHAR verb[MAX_PATH]; - if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass)) + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, KEY_READ, &hkeyClass)) return SE_ERR_NOASSOC; if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, ARRAY_SIZE(verb))) return SE_ERR_NOASSOC; RegCloseKey(hkeyClass); /* Looking for ...buffer\shell\\command */ - wcscat(classname, L"\\shell\\"); + wcscat(classname, L"\\shell\\"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI wcscat(classname, verb); wcscat(classname, L"\\command"); @@ -1361,48 +1374,21 @@ HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpR /* FIXME: is this already implemented somewhere else? */ static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei) { - LPCWSTR ext = NULL, lpClass = NULL; - CHeapPtr cls; - DWORD type = 0, sz = 0; - HKEY hkey = 0; - LONG r; - - if (sei->fMask & SEE_MASK_CLASSALL) + if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSKEY) return sei->hkeyClass; + HKEY hKey = NULL; if (sei->fMask & SEE_MASK_CLASSNAME) - lpClass = sei->lpClass; - else { - ext = PathFindExtensionW(sei->lpFile); - TRACE("ext = %s\n", debugstr_w(ext)); - if (!ext) - return hkey; - - r = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey); - if (r != ERROR_SUCCESS) - return hkey; - - r = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &sz); - if (r == ERROR_SUCCESS && type == REG_SZ) - { - sz += sizeof (WCHAR); - cls.Allocate(sz / sizeof(WCHAR)); - cls[0] = 0; - RegQueryValueExW(hkey, NULL, 0, &type, (LPBYTE)(LPWSTR)cls, &sz); - } - - RegCloseKey( hkey ); - lpClass = cls; + TRACE("class = %s\n", debugstr_w(sei->lpClass)); + RegOpenKeyExW(HKEY_CLASSES_ROOT, sei->lpClass, 0, KEY_READ, &hKey); + return hKey; } - - TRACE("class = %s\n", debugstr_w(lpClass)); - - hkey = 0; - if (lpClass) - RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey); - - return hkey; + PCWSTR ext = PathFindExtensionW(sei->lpFile); + TRACE("ext = %s\n", debugstr_w(ext)); + if (!StrIsNullOrEmpty(ext) && SUCCEEDED(HCR_GetProgIdKeyOfExtension(ext, &hKey, FALSE))) + return hKey; + return NULL; } static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr& dataObj) @@ -1410,7 +1396,7 @@ static HRESULT shellex_get_dataobj( LPSHELLEXECUTEINFOW sei, CComPtr allocatedPidl; LPITEMIDLIST pidl = NULL; - if (sei->fMask & SEE_MASK_CLASSALL) + if (sei->fMask & SEE_MASK_CLASSALL) // FIXME: This makes no sense? SEE_MASK_IDLIST? { pidl = (LPITEMIDLIST)sei->lpIDList; } @@ -1680,7 +1666,7 @@ static LONG ShellExecute_FromContextMenuHandlers( LPSHELLEXECUTEINFOW sei ) return r; } -static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc); +static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc); static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc) { @@ -1905,7 +1891,7 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEIN if (psei->lpVerb && *psei->lpVerb) len += lstrlenW(psei->lpVerb); else - len += lstrlenW(L"open"); + len += lstrlenW(L"open"); // FIXME: Use HCR_GetExecuteCommandW or AssocAPI lpstrProtocol.Allocate(len); memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR)); lpstrProtocol[iSize] = '\0'; @@ -1919,13 +1905,12 @@ static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEIN return retval; } -static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) +static void do_error_dialog(UINT_PTR retval, HWND hwnd, PCWSTR filename) { WCHAR msg[2048]; DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 }; - DWORD error_code; + const DWORD error_code = GetLastError(); - error_code = GetLastError(); if (retval == SE_ERR_NOASSOC) LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, ARRAY_SIZE(msg)); else @@ -1938,6 +1923,7 @@ static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename) (va_list*)msgArguments); MessageBoxW(hwnd, msg, NULL, MB_ICONERROR); + SetLastError(error_code); // Restore } static WCHAR *expand_environment( const WCHAR *str ) @@ -1971,6 +1957,7 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) BOOL appKnownSingular = FALSE; /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */ + sei->hProcess = NULL; SHELLEXECUTEINFOW sei_tmp = *sei; TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n", @@ -1980,8 +1967,6 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ? debugstr_w(sei_tmp.lpClass) : "not used"); - sei->hProcess = NULL; - /* make copies of all path/command strings */ CHeapPtr wszApplicationName; DWORD dwApplicationNameLen = MAX_PATH + 2; @@ -2330,7 +2315,8 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) /* if so, prefix lpFile with http:// and call ShellExecute */ WCHAR lpstrTmpFile[256]; strcpyW(lpstrTmpFile, L"http://"); - strcatW(lpstrTmpFile, lpFile); + strcatW(lpstrTmpFile, lpFile); // FIXME: Possible buffer overflow + // FIXME: This will not correctly return the hProcess to the caller retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0); } @@ -2338,18 +2324,10 @@ static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc) if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI)) { - OPENASINFO Info; - - //FIXME - // need full path - - Info.pcszFile = wszApplicationName; - Info.pcszClass = NULL; - Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC; - - //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK) - DBG_UNREFERENCED_LOCAL_VARIABLE(Info); - do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName); + 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); } sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval); @@ -2393,7 +2371,11 @@ static DWORD ShellExecute_Normal(_Inout_ LPSHELLEXECUTEINFOW sei) { // FIXME - return SHELL_execute(sei, SHELL_ExecuteW) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND; + if (SHELL_execute(sei, SHELL_ExecuteW)) + return ERROR_SUCCESS; + DWORD err = GetLastError(); + assert(err); + return err ? err : ERROR_FILE_NOT_FOUND; } static VOID @@ -2592,14 +2574,8 @@ EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFil EXTERN_C void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow) { - OPENASINFO info; + OPENASINFO info = { cmdline, NULL, OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC }; TRACE("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow); - - ZeroMemory(&info, sizeof(info)); - info.pcszFile = cmdline; - info.pcszClass = NULL; - info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT | OAIF_EXEC; - SHOpenWithDialog(hwnd, &info); } @@ -2700,7 +2676,7 @@ HRESULT WINAPI ShellExecCmdLine( if (dwSeclFlags & SECL_RUNAS) { dwSize = 0; - hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize); + hr = AssocQueryStringW(ASSOCF_NONE, ASSOCSTR_COMMAND, lpCommand, L"runas", NULL, &dwSize); if (SUCCEEDED(hr) && dwSize != 0) { pszVerb = L"runas"; diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h index 80961832717..2c4e4dc614a 100644 --- a/dll/win32/shell32/utils.h +++ b/dll/win32/shell32/utils.h @@ -54,6 +54,20 @@ RegKeyExists(HKEY hKey, LPCWSTR Path) return ret; } +inline UINT +RegQueryDword(HKEY hKey, PCWSTR pszPath, PCWSTR pszName, DWORD *pnVal) +{ + DWORD cb = sizeof(*pnVal); + return RegGetValueW(hKey, pszPath, pszName, RRF_RT_REG_DWORD, NULL, pnVal, &cb); +} + +inline DWORD +RegGetDword(HKEY hKey, PCWSTR pszPath, PCWSTR pszName, DWORD nDefVal) +{ + DWORD nVal; + return RegQueryDword(hKey, pszPath, pszName, &nVal) == ERROR_SUCCESS ? nVal : nDefVal; +} + inline DWORD RegSetOrDelete(HKEY hKey, LPCWSTR Name, DWORD Type, LPCVOID Data, DWORD Size) { diff --git a/dll/win32/shlwapi/assoc.c b/dll/win32/shlwapi/assoc.c index f2749fe559b..ad5b8cddf1a 100644 --- a/dll/win32/shlwapi/assoc.c +++ b/dll/win32/shlwapi/assoc.c @@ -40,7 +40,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); /* Default IQueryAssociations::Init() flags */ #ifdef __REACTOS__ #define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME | ASSOCF_INIT_DEFAULTTOSTAR | \ - ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_NOREMAPCLSID) + ASSOCF_INIT_DEFAULTTOFOLDER | ASSOCF_INIT_NOREMAPCLSID | \ + ASSOCF_INIT_IGNOREUNKNOWN) #else #define SHLWAPI_DEF_ASSOCF (ASSOCF_INIT_BYEXENAME|ASSOCF_INIT_DEFAULTTOSTAR| \ ASSOCF_INIT_DEFAULTTOFOLDER) @@ -377,8 +378,12 @@ HRESULT WINAPI AssocQueryKeyW(ASSOCF cfFlags, ASSOCKEY assockey, LPCWSTR pszAsso hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc ); if (FAILED(hRet)) return hRet; +#ifdef __REACTOS__ + hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF, pszAssoc, NULL, NULL); +#else cfFlags &= SHLWAPI_DEF_ASSOCF; hRet = IQueryAssociations_Init(lpAssoc, cfFlags, pszAssoc, NULL, NULL); +#endif if (SUCCEEDED(hRet)) hRet = IQueryAssociations_GetKey(lpAssoc, cfFlags, assockey, pszExtra, phkeyOut); @@ -542,8 +547,12 @@ HRESULT WINAPI AssocQueryStringByKeyW(ASSOCF cfFlags, ASSOCSTR str, HKEY hkAssoc hRet = AssocCreate( CLSID_QueryAssociations, &IID_IQueryAssociations, (void **)&lpAssoc ); if (FAILED(hRet)) return hRet; +#ifdef __REACTOS__ + hRet = IQueryAssociations_Init(lpAssoc, cfFlags & SHLWAPI_DEF_ASSOCF, 0, hkAssoc, NULL); +#else cfFlags &= SHLWAPI_DEF_ASSOCF; hRet = IQueryAssociations_Init(lpAssoc, cfFlags, 0, hkAssoc, NULL); +#endif if (SUCCEEDED(hRet)) hRet = IQueryAssociations_GetString(lpAssoc, cfFlags, str, pszExtra,