From 81d845fb2c87c6a40f139f64267756bebdb39639 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 30 Mar 2025 20:28:31 +0900 Subject: [PATCH] [SHELL32] Implement PathProcessCommandW (#7818) Follow-up of #7815. This function affects ShellExec_RunDLLA/W implementation. JIRA issue: CORE-17659 - Implement PathProcessCommandW in shlexec.cpp. - Adapt utils.h to non-C++. --- dll/win32/shell32/shlexec.cpp | 124 +++++++++++++++++++++++++- dll/win32/shell32/utils.h | 14 ++- dll/win32/shell32/wine/shell32_main.h | 8 +- dll/win32/shell32/wine/shellpath.c | 2 + 4 files changed, 143 insertions(+), 5 deletions(-) diff --git a/dll/win32/shell32/shlexec.cpp b/dll/win32/shell32/shlexec.cpp index cf04d935742..6010c4a4620 100644 --- a/dll/win32/shell32/shlexec.cpp +++ b/dll/win32/shell32/shlexec.cpp @@ -2976,6 +2976,127 @@ RealShellExecuteW( 0); } +/************************************************************************* + * PathProcessCommandW [Internal] + * + * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-pathprocesscommand + * @see ./wine/shellpath.c + */ +EXTERN_C LONG +PathProcessCommandW( + _In_ PCWSTR pszSrc, + _Out_writes_opt_(dwBuffSize) PWSTR pszDest, + _In_ INT cchDest, + _In_ DWORD dwFlags) +{ + TRACE("%s, %p, %d, 0x%X\n", wine_dbgstr_w(pszSrc), pszDest, cchDest, dwFlags); + + if (!pszSrc) + return -1; + + CStringW szPath; + PCWSTR pchArg = NULL; + + if (*pszSrc == L'"') // Quoted? + { + ++pszSrc; + + PCWSTR pch = wcschr(pszSrc, L'"'); + if (pch) + { + szPath.SetString(pszSrc, pch - pszSrc); + pchArg = pch + 1; + } + else + { + szPath = pszSrc; + } + + if ((dwFlags & PPCF_FORCEQUALIFY) || PathIsRelativeW(szPath)) + { + BOOL ret = PathResolveW(szPath.GetBuffer(MAX_PATH), NULL, PRF_TRYPROGRAMEXTENSIONS); + szPath.ReleaseBuffer(); + if (!ret) + return -1; + } + } + else // Not quoted? + { + BOOL resolved = FALSE; + BOOL resolveRelative = PathIsRelativeW(pszSrc) || (dwFlags & PPCF_FORCEQUALIFY); + INT cchPath = 0; + + for (INT ich = 0; ; ++ich) + { + szPath += pszSrc[ich]; + + if (pszSrc[ich] && pszSrc[ich] != L' ') + continue; + + szPath = szPath.Left(ich); + + if (resolveRelative && + !PathResolveW(szPath.GetBuffer(MAX_PATH), NULL, PRF_TRYPROGRAMEXTENSIONS)) + { + szPath.ReleaseBuffer(); + szPath += pszSrc[ich]; + } + else + { + szPath.ReleaseBuffer(); + + DWORD attrs = GetFileAttributesW(szPath); + if (attrs != INVALID_FILE_ATTRIBUTES && + (!(attrs & FILE_ATTRIBUTE_DIRECTORY) || !(dwFlags & PPCF_NODIRECTORIES))) + { + resolved = TRUE; + pchArg = pszSrc + ich; + + if (!(dwFlags & PPCF_LONGESTPOSSIBLE)) + break; + + cchPath = ich; + break; + } + else if (!resolveRelative) + { + szPath += pszSrc[ich]; + } + } + + if (!szPath[ich]) + { + szPath.ReleaseBuffer(); // Remove excessive '\0' + break; + } + } + + if (!resolved) + return -1; + + if (cchPath && (dwFlags & PPCF_LONGESTPOSSIBLE)) + { + szPath = szPath.Left(cchPath); + pchArg = pszSrc + cchPath; + } + } + + BOOL needsQuoting = (dwFlags & PPCF_ADDQUOTES) && wcschr(szPath, L' '); + CStringW result = needsQuoting ? (L"\"" + szPath + L"\"") : szPath; + + if (pchArg && (dwFlags & PPCF_ADDARGUMENTS)) + result += pchArg; + + LONG requiredSize = result.GetLength() + 1; + if (!pszDest) + return requiredSize; + + if (requiredSize > cchDest || StringCchCopyW(pszDest, cchDest, result) != S_OK) + return -1; + + return requiredSize; +} + // The common helper of ShellExec_RunDLLA and ShellExec_RunDLLW static VOID ShellExec_RunDLL_Helper( @@ -3003,7 +3124,8 @@ ShellExec_RunDLL_Helper( } WCHAR szPath[2 * MAX_PATH]; - if (PathProcessCommandAW(pszCmdLine, szPath, _countof(szPath), L'C') == -1) + DWORD dwFlags = PPCF_FORCEQUALIFY | PPCF_INCLUDEARGS | PPCF_ADDQUOTES; + if (PathProcessCommandW(pszCmdLine, szPath, _countof(szPath), dwFlags) == -1) StrCpyNW(szPath, pszCmdLine, _countof(szPath)); // Split arguments from the path diff --git a/dll/win32/shell32/utils.h b/dll/win32/shell32/utils.h index 4f13d541f8f..80961832717 100644 --- a/dll/win32/shell32/utils.h +++ b/dll/win32/shell32/utils.h @@ -7,6 +7,14 @@ #pragma once +#ifndef OPTIONAL_ + #ifdef __cplusplus + #define OPTIONAL_(arg) = arg + #else + #define OPTIONAL_(arg) + #endif +#endif + #ifdef __cplusplus static inline LPWSTR SHStrDupW(LPCWSTR Src) @@ -50,15 +58,15 @@ inline DWORD RegSetOrDelete(HKEY hKey, LPCWSTR Name, DWORD Type, LPCVOID Data, DWORD Size) { if (Data) - return RegSetValueExW(hKey, Name, 0, Type, LPBYTE(Data), Size); + return RegSetValueExW(hKey, Name, 0, Type, (LPBYTE)Data, Size); else return RegDeleteValueW(hKey, Name); } static inline DWORD -RegSetString(HKEY hKey, LPCWSTR Name, LPCWSTR Str, DWORD Type = REG_SZ) +RegSetString(HKEY hKey, LPCWSTR Name, LPCWSTR Str, DWORD Type OPTIONAL_(REG_SZ)) { - return RegSetValueExW(hKey, Name, 0, Type, LPBYTE(Str), (lstrlenW(Str) + 1) * sizeof(WCHAR)); + return RegSetValueExW(hKey, Name, 0, Type, (LPBYTE)Str, (lstrlenW(Str) + 1) * sizeof(WCHAR)); } typedef struct diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index b631c67a5ef..ec6c1f5d01c 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -76,10 +76,16 @@ BOOL HCR_GetFolderAttributes(LPCITEMIDLIST pidlFolder, LPDWORD dwAttributes) DEC HRESULT SHELL32_AssocGetFSDirectoryDescription(PWSTR Buf, UINT cchBuf); HRESULT SHELL32_AssocGetFileDescription(PCWSTR Name, PWSTR Buf, UINT cchBuf); - DWORD WINAPI ParseFieldA(LPCSTR src, DWORD nField, LPSTR dst, DWORD len) DECLSPEC_HIDDEN; DWORD WINAPI ParseFieldW(LPCWSTR src, DWORD nField, LPWSTR dst, DWORD len) DECLSPEC_HIDDEN; +LONG +PathProcessCommandW( + _In_ PCWSTR pszSrc, + _Out_writes_opt_(dwBuffSize) PWSTR pszDest, + _In_ INT cchDest, + _In_ DWORD dwFlags); + /**************************************************************************** * Class constructors */ diff --git a/dll/win32/shell32/wine/shellpath.c b/dll/win32/shell32/wine/shellpath.c index 1ccecd9529b..3a0b5097622 100644 --- a/dll/win32/shell32/wine/shellpath.c +++ b/dll/win32/shell32/wine/shellpath.c @@ -1130,6 +1130,7 @@ static LONG PathProcessCommandA ( return strlen(lpszPath); } +#ifndef __REACTOS__ // See ../shlexec.cpp /************************************************************************* * PathProcessCommandW */ @@ -1145,6 +1146,7 @@ static LONG PathProcessCommandW ( if(lpszBuff) strcpyW(lpszBuff, lpszPath); return strlenW(lpszPath); } +#endif /************************************************************************* * PathProcessCommand (SHELL32.653)