ReactOS 0.4.17-dev-482-g1b46029
evalcmd.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Shell
3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4 * PURPOSE: SHEvaluateSystemCommandTemplate
5 * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8#include <windef.h>
9#include <shlobj.h>
10#include <shlwapi.h>
11#include <shlguid_undoc.h>
12#include <shlobj_undoc.h>
13#include <shlwapi_undoc.h>
14#include <strsafe.h>
15#include <pathcch.h>
16#include <assert.h>
17#include "evalcmd.h"
18
19#include <wine/debug.h>
21
22#define PATH_VALID_CHARS \
23 (PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \
24 PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID)
25
30{
31 PCWSTR pch;
32 if (*lpString == L'"')
33 {
34 pch = StrChrW(lpString + 1, L'"');
35 if (pch)
36 {
37 ++pch;
38 if (*pch == L' ')
39 ++pch;
40 return pch;
41 }
42 }
43 else
44 {
45 pch = StrChrW(lpString, L' ');
46 if (pch)
47 return pch + 1;
48 }
49 return &lpString[lstrlenW(lpString)];
50}
51
53_PathCopyExeAndTrimWhiteSpaces(PWSTR pszBuff, size_t cchBuff, PCWSTR pszSrc, size_t cchSrc)
54{
55 HRESULT hr = StringCchCopyNW(pszBuff, cchBuff, pszSrc, cchSrc);
56 if (SUCCEEDED(hr))
57 StrTrimW(pszBuff, L" \t");
58 return hr;
59}
60
65{
66 WCHAR pszPath[MAX_PATH];
68 INT cch = lstrlenW(pszPath);
69 return StrCmpNIW(lpString, pszPath, cch) == 0;
70}
71
72// This function attempts to find where the "arguments" portion of a command-line path string
74{
75 PCWSTR pSpaceStart = NULL;
76 BOOL bValid = TRUE;
77
78 for (; *pszPath && bValid; ++pszPath)
79 {
80 switch (*pszPath)
81 {
82 case L' ':
83 if (!pSpaceStart)
84 pSpaceStart = pszPath;
85 break;
86
87 case L'"':
88 case L'%':
89 bValid = FALSE;
90 break;
91
92 case L'\\':
93 bValid = !PathIsUNCW(pszPath);
94 if (bValid)
95 pSpaceStart = NULL;
96 break;
97
98 default:
99 bValid = PathIsValidCharW(*pszPath, PATH_VALID_CHARS);
100 break;
101 }
102 }
103
104 if (pSpaceStart)
105 {
106 while (*pSpaceStart == L' ')
107 ++pSpaceStart;
108 return pSpaceStart;
109 }
110
111 return bValid ? pszPath : NULL;
112}
113
114static VOID _MakeAppPathKey(PCWSTR pszPath, PWSTR pszDest, UINT cchDest)
115{
117 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths",
118 pszPath, PATHCCH_NONE);
119 if (SUCCEEDED(hr))
120 PathCchAddExtension(pszDest, cchDest, L".exe");
121}
122
123static BOOL _GetAppPath(PCWSTR pszPath, PWSTR pszValue, DWORD cchValue)
124{
125 WCHAR szSubKey[MAX_PATH];
126 _MakeAppPathKey(pszPath, szSubKey, _countof(szSubKey));
127 DWORD cbData = cchValue * sizeof(WCHAR);
129 return error == ERROR_SUCCESS;
130}
131
133{
136
138 DWORD attrs;
139 if (!PathFileExistsDefExtAndAttributesW(szPath, dwWhich, &attrs) ||
140 (attrs & FILE_ATTRIBUTE_DIRECTORY))
141 {
142 return CO_E_APPNOTFOUND;
143 }
144 return S_OK;
145}
146
148_PathFindInFolder(_In_ INT csidl, _In_ PCWSTR pszSrc, _Out_ PWSTR pszPath, _In_ UINT cchPath)
149{
150 WCHAR szDir[MAX_PATH];
151 HRESULT hr = SHGetFolderPathW(0, csidl, 0, 0, szDir);
152 if (FAILED(hr))
153 return hr;
154
155 hr = PathCchCombineEx(pszPath, cchPath, szDir, pszSrc, PATHCCH_NONE);
156 if (FAILED(hr))
157 return hr;
158
159 return _PathExeExists(pszPath);
160}
161
163{
166 if (FAILED(hr))
168 if (FAILED(hr))
169 return hr;
170 return StringCchCopyW(pszPath, cchPath, szPath);
171}
172
173/*************************************************************************
174 * SHEvaluateSystemCommandTemplate [SHELL32.@] (Vista+)
175 * SHEvaluateSystemCommandTemplate [SHLWAPI.552] (XP SP1 and SP2)
176 *
177 * https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shevaluatesystemcommandtemplate
178 * https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/um/shellapi.h#L525
179 */
183 _In_ PCWSTR pszCmdTemplate,
184 _Outptr_ PWSTR *ppszApplication,
185 _Outptr_opt_ PWSTR *ppszCommandLine,
186 _Outptr_opt_ PWSTR *ppszParameters)
187{
188 HRESULT hr;
189 BOOL bQuoted;
190 WCHAR szExe[MAX_PATH], szProgram[MAX_PATH];
191
192 PCWSTR pszArgs = _PathGetArgsLikeCreateProcess(pszCmdTemplate);
193 UINT cchArgs = (UINT)(pszArgs - pszCmdTemplate);
194 hr = _PathCopyExeAndTrimWhiteSpaces(szExe, _countof(szExe), pszCmdTemplate, cchArgs);
195 if (FAILED(hr))
196 goto Exit;
197
198 // Unquote if necessary
199 bQuoted = (szExe[0] == L'"');
200 if (bQuoted)
201 PathUnquoteSpacesW(szExe);
202
203 hr = StringCchCopyW(szProgram, _countof(szProgram), szExe);
205
206 if (PathIsAbsolute(szExe))
207 {
208 if (bQuoted)
209 {
210 hr = _PathExeExists(szExe);
211 }
212 else // Not quoted
213 {
214 if (_PathMatchesSuspicious(szExe)) // ProgramFiles-likely?
216 else
217 hr = _PathExeExists(szExe);
218 }
219
220 // Detect where the full executable path ends and the arguments start
221 while (FAILED(hr))
222 {
223 if (bQuoted || !*pszArgs)
224 break;
225
226 pszArgs = _PathGuessNextBestArgs(pszArgs);
227 if (!pszArgs)
228 break;
229
230 cchArgs = (UINT)(pszArgs - pszCmdTemplate);
231 hr = _PathCopyExeAndTrimWhiteSpaces(szExe, _countof(szExe), pszCmdTemplate, cchArgs);
232 if (FAILED(hr))
233 break;
234
235 hr = _PathExeExists(szExe);
236 }
237 }
238 else
239 {
240 if (!PathIsFileSpecW(szExe))
241 {
243 goto Exit;
244 }
245
246 if (_GetAppPath(szExe, szExe, _countof(szExe)))
247 {
248 hr = StringCchCopyW(szProgram, _countof(szProgram), PathFindFileNameW(szExe));
249 }
250 else if (SHWindowsPolicyEx(POLID_UsePathEnvVarForCommandTemplates, FALSE))
251 {
253 }
254 else
255 {
256 hr = _PathFindInSystem(szExe, _countof(szExe));
257 }
258 }
259
260Exit:
261 *ppszApplication = NULL;
262 if (ppszCommandLine)
263 *ppszCommandLine = NULL;
264 if (ppszParameters)
265 *ppszParameters = NULL;
266
267 if (!pszArgs)
268 pszArgs = L"";
269
270 // Create output strings
271 if (SUCCEEDED(hr))
272 hr = SHStrDupW(szExe, ppszApplication);
273
274 if (SUCCEEDED(hr) && ppszCommandLine)
275 {
276 size_t cch = lstrlenW(szProgram) + lstrlenW(pszArgs) + 4; // 4 for '"', '"', ' ', NUL
277 hr = SHCoAlloc(cch * sizeof(WCHAR), (PVOID*)ppszCommandLine);
278 if (SUCCEEDED(hr))
279 hr = StringCchPrintfW(*ppszCommandLine, cch, L"\"%s\" %s", szProgram, pszArgs);
280 }
281
282 if (SUCCEEDED(hr) && ppszParameters)
283 hr = SHStrDupW(pszArgs, ppszParameters);
284
285 if (FAILED(hr))
286 {
287 // Clean up
288 if (*ppszApplication)
289 {
290 CoTaskMemFree(*ppszApplication);
291 *ppszApplication = NULL;
292 }
293 if (ppszCommandLine && *ppszCommandLine)
294 {
295 CoTaskMemFree(*ppszCommandLine);
296 *ppszCommandLine = NULL;
297 }
298 if (ppszParameters && *ppszParameters)
299 {
300 CoTaskMemFree(*ppszParameters);
301 *ppszParameters = NULL;
302 }
303 }
304
305 return hr;
306}
#define WINE_DEFAULT_DEBUG_CHANNEL(t)
Definition: precomp.h:23
#define EXTERN_C
Definition: basetyps.h:12
HRESULT hr
Definition: delayimp.cpp:582
#define ERROR_SUCCESS
Definition: deptool.c:10
static LSTATUS(WINAPI *pRegDeleteTreeW)(HKEY
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
void WINAPI CoTaskMemFree(void *ptr)
Definition: malloc.c:389
LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
Definition: string.c:464
INT WINAPI StrCmpNIW(LPCWSTR lpszStr, LPCWSTR lpszComp, INT iLen)
Definition: string.c:307
#define MAX_PATH
Definition: compat.h:34
#define lstrlenW
Definition: compat.h:750
BOOL WINAPI PathIsUNCW(const WCHAR *path)
Definition: path.c:989
void WINAPI PathUnquoteSpacesW(WCHAR *path)
Definition: path.c:1982
WCHAR *WINAPI PathFindFileNameW(const WCHAR *path)
Definition: path.c:1677
HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension)
Definition: path.c:542
BOOL WINAPI PathIsValidCharW(WCHAR c, DWORD class)
Definition: path.c:2197
BOOL WINAPI PathIsFileSpecW(const WCHAR *path)
Definition: path.c:1818
HRESULT WINAPI PathCchCombineEx(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2, DWORD flags)
Definition: path.c:674
BOOL WINAPI StrTrimW(WCHAR *str, const WCHAR *trim)
Definition: string.c:804
#define assert(_expr)
Definition: assert.h:32
HRESULT WINAPI SHStrDupW(const WCHAR *src, WCHAR **dest)
Definition: main.c:1692
DWORD WINAPI SHGetValueW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, DWORD *type, void *data, DWORD *data_len)
Definition: main.c:2222
HRESULT WINAPI SHGetFolderPathW(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath)
Definition: shellpath.c:2716
BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs, DWORD dwWhich)
Definition: path.c:404
BOOL WINAPI PathFileExistsDefExtAndAttributesW(_Inout_ LPWSTR pszPath, _In_ DWORD dwWhich, _Out_opt_ LPDWORD pdwFileAttributes)
Definition: utils.cpp:661
#define L(x)
Definition: resources.c:13
EXTERN_C HRESULT _PathFindInFolder(_In_ INT csidl, _In_ PCWSTR pszSrc, _Out_ PWSTR pszPath, _In_ UINT cchPath)
Definition: evalcmd.cpp:148
EXTERN_C HRESULT _PathCopyExeAndTrimWhiteSpaces(PWSTR pszBuff, size_t cchBuff, PCWSTR pszSrc, size_t cchSrc)
Definition: evalcmd.cpp:53
static VOID _MakeAppPathKey(PCWSTR pszPath, PWSTR pszDest, UINT cchDest)
Definition: evalcmd.cpp:114
static HRESULT _PathExeExists(_In_ PCWSTR pszPath)
Definition: evalcmd.cpp:132
static BOOL _PathMatchesSuspicious(PCWSTR lpString)
"Program Files" contains space. It needs special handling. This function will detect it.
Definition: evalcmd.cpp:64
static PCWSTR _PathGetArgsLikeCreateProcess(PCWSTR lpString)
Definition: evalcmd.cpp:29
static BOOL _GetAppPath(PCWSTR pszPath, PWSTR pszValue, DWORD cchValue)
Definition: evalcmd.cpp:123
EXTERN_C HRESULT _PathFindInSystem(_Inout_ PWSTR pszPath, _In_ UINT cchPath)
Definition: evalcmd.cpp:162
static PCWSTR _PathGuessNextBestArgs(PCWSTR pszPath)
Definition: evalcmd.cpp:73
EXTERN_C HRESULT WINAPI SHEvaluateSystemCommandTemplate(_In_ PCWSTR pszCmdTemplate, _Outptr_ PWSTR *ppszApplication, _Outptr_opt_ PWSTR *ppszCommandLine, _Outptr_opt_ PWSTR *ppszParameters)
Definition: evalcmd.cpp:182
#define PATH_VALID_CHARS
Definition: evalcmd.cpp:22
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
unsigned int UINT
Definition: sysinfo.c:13
#define S_OK
Definition: intsafe.h:52
#define SUCCEEDED(hr)
Definition: intsafe.h:50
#define FAILED(hr)
Definition: intsafe.h:51
#define error(str)
Definition: mkdosfs.c:1605
#define pch(ap)
Definition: match.c:418
LPCWSTR szPath
Definition: env.c:37
#define _Inout_
Definition: no_sal2.h:162
#define _Outptr_opt_
Definition: no_sal2.h:264
#define _Outptr_
Definition: no_sal2.h:262
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define FILE_ATTRIBUTE_DIRECTORY
Definition: nt_native.h:705
@ PATHCCH_NONE
Definition: pathcch.h:39
short WCHAR
Definition: pedump.c:58
_In_opt_ _In_opt_ _In_ _In_ DWORD cbData
Definition: shlwapi.h:761
_In_ INT cchDest
Definition: shlwapi.h:1161
_In_opt_ LPCSTR _In_opt_ LPCSTR pszValue
Definition: shlwapi.h:783
_In_ UINT _In_ UINT cch
Definition: shellapi.h:432
#define CSIDL_PROGRAM_FILES
Definition: shlobj.h:2227
#define CSIDL_SYSTEM
Definition: shlobj.h:2226
#define CSIDL_WINDOWS
Definition: shlobj.h:2225
#define WHICH_BAT
#define WHICH_CMD
#define WHICH_COM
static HRESULT SHCoAlloc(_In_ SIZE_T cb, _Outptr_ PVOID *ppData)
static DWORD SHWindowsPolicyEx(_In_ REFGUID rpolid, _In_ DWORD dwDefaultValue)
#define WHICH_EXE
#define WHICH_PIF
#define WHICH_OPTIONAL
static BOOL PathIsAbsolute(_In_ PCWSTR pszPath)
#define _countof(array)
Definition: sndvol32.h:70
static void Exit(void)
Definition: sock.c:1330
STRSAFEAPI StringCchPrintfW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszFormat,...)
Definition: strsafe.h:530
STRSAFEAPI StringCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:149
STRSAFEAPI StringCchCopyNW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc, size_t cchToCopy)
Definition: strsafe.h:236
uint16_t * PWSTR
Definition: typedefs.h:56
const uint16_t * PCWSTR
Definition: typedefs.h:57
int32_t INT
Definition: typedefs.h:58
#define WINAPI
Definition: msvc.h:6
static HRESULT HRESULT_FROM_WIN32(unsigned int x)
Definition: winerror.h:210
#define ERROR_PATH_NOT_FOUND
Definition: winerror.h:228
#define E_ACCESSDENIED
Definition: winerror.h:4116
#define CO_E_APPNOTFOUND
Definition: winerror.h:3921
_In_ DWORD _In_ int cchSrc
Definition: winnls.h:1275
#define HKEY_LOCAL_MACHINE
Definition: winreg.h:12