From a1157f86f2766e2916147965293bc532e70f66af Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Sat, 15 Feb 2025 14:27:51 +0100 Subject: [PATCH] [SHELL32] OpenWith should use dll path instead when the command starts with rundll32 (#7712) CORE-16980 --- dll/win32/shell32/COpenWithMenu.cpp | 77 +++++++++++++++++++----- dll/win32/shell32/dialogs/filedefext.cpp | 10 ++- dll/win32/shell32/precomp.h | 1 + 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/dll/win32/shell32/COpenWithMenu.cpp b/dll/win32/shell32/COpenWithMenu.cpp index 96a73dd822d..3e081de625b 100644 --- a/dll/win32/shell32/COpenWithMenu.cpp +++ b/dll/win32/shell32/COpenWithMenu.cpp @@ -31,6 +31,56 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath); +static SIZE_T PathGetAppFromCommandLine(LPCWSTR pszIn, LPWSTR pszOut, SIZE_T cchMax) +{ + SIZE_T count = 0; + WCHAR stop = ' '; + if (pszIn[0] == '"') + stop = *(pszIn++); + + for (LPCWSTR pwszSrc = pszIn; *pwszSrc && *pwszSrc != stop; ++pwszSrc) + { + if (++count >= cchMax) + return 0; + *(pszOut++) = *pwszSrc; + } + *pszOut = UNICODE_NULL; + return count; +} + +HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax) +{ + WCHAR szDll[MAX_PATH + 100]; + if (!PathGetAppFromCommandLine(pszCmd, szDll, _countof(szDll))) + return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); + + PWSTR pszName = PathFindFileNameW(szDll); + if (_wcsicmp(pszName, L"rundll32") && _wcsicmp(pszName, L"rundll32.exe")) + return E_UNEXPECTED; + + PCWSTR pszDllStart = pszCmd + (pszName - szDll) + lstrlenW(pszName); + + if (*pszDllStart == '\"') + ++pszDllStart; // Skip possible end quote of ..\rundll32.exe" foo.dll,func + while (*pszDllStart <= ' ' && *pszDllStart) + ++pszDllStart; + if (PathGetAppFromCommandLine(pszDllStart, szDll, _countof(szDll))) + { + BOOL quoted = *pszDllStart == '\"'; + PWSTR pszComma = szDll + lstrlenW(szDll); + while (!quoted && pszComma > szDll && *pszComma != ',' && *pszComma != '\\' && *pszComma != '/') + --pszComma; + SIZE_T cch = pszComma - szDll; + if (cch <= cchMax && (quoted || *pszComma == ',')) + { + *pszComma = UNICODE_NULL; + lstrcpynW(pszOut, szDll, cchMax); + return S_OK; + } + } + return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); +} + class COpenWithList { public: @@ -396,26 +446,23 @@ BOOL COpenWithList::LoadInfo(COpenWithList::SApp *pApp) BOOL COpenWithList::GetPathFromCmd(LPWSTR pwszAppPath, LPCWSTR pwszCmd) { - WCHAR wszBuf[MAX_PATH], *pwszDest = wszBuf; + WCHAR wszBuf[MAX_PATH]; /* Remove arguments */ - if (pwszCmd[0] == '"') - { - for(LPCWSTR pwszSrc = pwszCmd + 1; *pwszSrc && *pwszSrc != '"'; ++pwszSrc) - *(pwszDest++) = *pwszSrc; - } - else - { - for(LPCWSTR pwszSrc = pwszCmd; *pwszSrc && *pwszSrc != ' '; ++pwszSrc) - *(pwszDest++) = *pwszSrc; - } + if (!PathGetAppFromCommandLine(pwszCmd, wszBuf, _countof(wszBuf))) + return FALSE; - *pwszDest = 0; + /* Replace rundll32.exe with the dll path */ + SHELL32_GetDllFromRundll32CommandLine(pwszCmd, wszBuf, _countof(wszBuf)); - /* Expand evn vers and optionally search for path */ + /* Expand env. vars and optionally search for path */ ExpandEnvironmentStrings(wszBuf, pwszAppPath, MAX_PATH); if (!PathFileExists(pwszAppPath)) - return SearchPath(NULL, pwszAppPath, NULL, MAX_PATH, pwszAppPath, NULL); + { + UINT cch = SearchPathW(NULL, pwszAppPath, NULL, MAX_PATH, pwszAppPath, NULL); + if (!cch || cch >= MAX_PATH) + return FALSE; + } return TRUE; } @@ -644,7 +691,7 @@ VOID COpenWithList::LoadRecommendedFromHKCU(LPCWSTR pwszExt) LoadMRUList(hKey); LoadProgIdList(hKey, pwszExt); - /* Handle "Aplication" value */ + /* Handle "Application" value */ DWORD cbBuf = sizeof(wszBuf); if (RegGetValueW(hKey, NULL, L"Application", RRF_RT_REG_SZ, NULL, wszBuf, &cbBuf) == ERROR_SUCCESS) { diff --git a/dll/win32/shell32/dialogs/filedefext.cpp b/dll/win32/shell32/dialogs/filedefext.cpp index e8beb7f936a..ce7f1dbe3f2 100644 --- a/dll/win32/shell32/dialogs/filedefext.cpp +++ b/dll/win32/shell32/dialogs/filedefext.cpp @@ -299,17 +299,23 @@ CFileDefExt::InitOpensWithField(HWND hwndDlg) BOOL bUnknownApp = TRUE; LPCWSTR pwszExt = PathFindExtensionW(m_wszPath); + // TODO: Use ASSOCSTR_EXECUTABLE with ASSOCF_REMAPRUNDLL | ASSOCF_IGNOREBASECLASS if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &dwSize) == ERROR_SUCCESS) { bUnknownApp = FALSE; StringCbCatW(wszBuf, sizeof(wszBuf), L"\\shell\\open\\command"); dwSize = sizeof(wszPath); + // FIXME: Missing FileExt check, see COpenWithList::SetDefaultHandler for details + // FIXME: Use HCR_GetDefaultVerbW to find the default verb if (RegGetValueW(HKEY_CLASSES_ROOT, wszBuf, L"", RRF_RT_REG_SZ, NULL, wszPath, &dwSize) == ERROR_SUCCESS) { /* Get path from command line */ ExpandEnvironmentStringsW(wszPath, wszBuf, _countof(wszBuf)); - PathRemoveArgs(wszBuf); - PathUnquoteSpacesW(wszBuf); + if (SHELL32_GetDllFromRundll32CommandLine(wszBuf, wszBuf, _countof(wszBuf)) != S_OK) + { + PathRemoveArgs(wszBuf); + PathUnquoteSpacesW(wszBuf); + } PathSearchAndQualify(wszBuf, wszPath, _countof(wszPath)); HICON hIcon; diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 267ed1d44c9..3a70cb606c8 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -293,6 +293,7 @@ BindCtx_RegisterObjectParam( BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath); BOOL PathIsValidElement(_In_ LPCWSTR pszPath); BOOL PathIsDosDevice(_In_ LPCWSTR pszName); +HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax); HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl); PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index);