ReactOS 0.4.17-dev-387-ga401325
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
18#define PATH_VALID_CHARS \
19 (PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \
20 PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID)
21
26{
27 PCWSTR pch;
28 if (*lpString == L'"')
29 {
30 pch = StrChrW(lpString + 1, L'"');
31 if (pch)
32 {
33 ++pch;
34 if (*pch == L' ')
35 ++pch;
36 return pch;
37 }
38 }
39 else
40 {
41 pch = StrChrW(lpString, L' ');
42 if (pch)
43 return pch + 1;
44 }
45 return &lpString[lstrlenW(lpString)];
46}
47
48static HRESULT
49_PathCopyExeAndTrimWhiteSpaces(PWSTR pszBuff, size_t cchBuff, PCWSTR pszSrc, size_t cchSrc)
50{
51 HRESULT hr = StringCchCopyNW(pszBuff, cchBuff, pszSrc, cchSrc);
52 if (SUCCEEDED(hr))
53 StrTrimW(pszBuff, L" \t");
54 return hr;
55}
56
61{
62 WCHAR pszPath[MAX_PATH];
64 INT cch = lstrlenW(pszPath);
65 return StrCmpNIW(lpString, pszPath, cch) == 0;
66}
67
68// This function attempts to find where the "arguments" portion of a command-line path string
70{
71 PCWSTR pSpaceStart = NULL;
72 BOOL bValid = TRUE;
73
74 for (; *pszPath && bValid; ++pszPath)
75 {
76 switch (*pszPath)
77 {
78 case L' ':
79 if (!pSpaceStart)
80 pSpaceStart = pszPath;
81 break;
82
83 case L'"':
84 case L'%':
85 bValid = FALSE;
86 break;
87
88 case L'\\':
89 bValid = !PathIsUNCW(pszPath);
90 if (bValid)
91 pSpaceStart = NULL;
92 break;
93
94 default:
95 bValid = PathIsValidCharW(*pszPath, PATH_VALID_CHARS);
96 break;
97 }
98 }
99
100 if (pSpaceStart)
101 {
102 while (*pSpaceStart == L' ')
103 ++pSpaceStart;
104 return pSpaceStart;
105 }
106
107 return bValid ? pszPath : NULL;
108}
109
110static VOID _MakeAppPathKey(PCWSTR pszPath, PWSTR pszDest, UINT cchDest)
111{
113 L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths",
114 pszPath, PATHCCH_NONE);
115 if (SUCCEEDED(hr))
116 PathCchAddExtension(pszDest, cchDest, L".exe");
117}
118
119static BOOL _GetAppPath(PCWSTR pszPath, PWSTR pszValue, DWORD cchValue)
120{
121 WCHAR szSubKey[MAX_PATH];
122 _MakeAppPathKey(pszPath, szSubKey, _countof(szSubKey));
123 DWORD cbData = cchValue * sizeof(WCHAR);
125 return error == ERROR_SUCCESS;
126}
127
129{
132
134 DWORD attrs;
135 if (!PathFileExistsDefExtAndAttributesW(szPath, dwWhich, &attrs) ||
136 (attrs & FILE_ATTRIBUTE_DIRECTORY))
137 {
138 return CO_E_APPNOTFOUND;
139 }
140 return S_OK;
141}
142
143static HRESULT
144_PathFindInFolder(_In_ INT csidl, _In_ PCWSTR pszSrc, _Out_ PWSTR pszPath, _In_ UINT cchPath)
145{
146 WCHAR szDir[MAX_PATH];
147 HRESULT hr = SHGetFolderPathW(0, csidl, 0, 0, szDir);
148 if (FAILED(hr))
149 return hr;
150
151 hr = PathCchCombineEx(pszPath, cchPath, szDir, pszSrc, PATHCCH_NONE);
152 if (FAILED(hr))
153 return hr;
154
155 return _PathExeExists(pszPath);
156}
157
159{
162 if (FAILED(hr))
164 if (FAILED(hr))
165 return hr;
166 return StringCchCopyW(pszPath, cchPath, szPath);
167}
168
169/*************************************************************************
170 * SHEvaluateSystemCommandTemplate [SHELL32.@] (Vista+)
171 * SHEvaluateSystemCommandTemplate [SHLWAPI.552] (XP SP1 and SP2)
172 *
173 * https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shevaluatesystemcommandtemplate
174 * https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.16299.0/um/shellapi.h#L525
175 */
179 _In_ PCWSTR pszCmdTemplate,
180 _Outptr_ PWSTR *ppszApplication,
181 _Outptr_opt_ PWSTR *ppszCommandLine,
182 _Outptr_opt_ PWSTR *ppszParameters)
183{
184 HRESULT hr;
185 BOOL bQuoted;
186 WCHAR szExe[MAX_PATH], szProgram[MAX_PATH];
187
188 PCWSTR pszArgs = _PathGetArgsLikeCreateProcess(pszCmdTemplate);
189 UINT cchArgs = (UINT)(pszArgs - pszCmdTemplate);
190 hr = _PathCopyExeAndTrimWhiteSpaces(szExe, _countof(szExe), pszCmdTemplate, cchArgs);
191 if (FAILED(hr))
192 goto Exit;
193
194 // Unquote if necessary
195 bQuoted = (szExe[0] == L'"');
196 if (bQuoted)
197 PathUnquoteSpacesW(szExe);
198
199 hr = StringCchCopyW(szProgram, _countof(szProgram), szExe);
201
202 if (PathIsAbsolute(szExe))
203 {
204 if (bQuoted)
205 {
206 hr = _PathExeExists(szExe);
207 }
208 else // Not quoted
209 {
210 if (_PathMatchesSuspicious(szExe)) // ProgramFiles-likely?
212 else
213 hr = _PathExeExists(szExe);
214 }
215
216 // Detect where the full executable path ends and the arguments start
217 while (FAILED(hr))
218 {
219 if (bQuoted || !*pszArgs)
220 break;
221
222 pszArgs = _PathGuessNextBestArgs(pszArgs);
223 if (!pszArgs)
224 break;
225
226 cchArgs = (UINT)(pszArgs - pszCmdTemplate);
227 hr = _PathCopyExeAndTrimWhiteSpaces(szExe, _countof(szExe), pszCmdTemplate, cchArgs);
228 if (FAILED(hr))
229 break;
230
231 hr = _PathExeExists(szExe);
232 }
233 }
234 else
235 {
236 if (!PathIsFileSpecW(szExe))
237 {
239 goto Exit;
240 }
241
242 if (_GetAppPath(szExe, szExe, _countof(szExe)))
243 {
244 hr = StringCchCopyW(szProgram, _countof(szProgram), PathFindFileNameW(szExe));
245 }
246 else if (SHWindowsPolicyEx(POLID_UsePathEnvVarForCommandTemplates, FALSE))
247 {
249 }
250 else
251 {
252 hr = _PathFindInSystem(szExe, _countof(szExe));
253 }
254 }
255
256Exit:
257 *ppszApplication = NULL;
258 if (ppszCommandLine)
259 *ppszCommandLine = NULL;
260 if (ppszParameters)
261 *ppszParameters = NULL;
262
263 if (!pszArgs)
264 pszArgs = L"";
265
266 // Create output strings
267 if (SUCCEEDED(hr))
268 hr = SHStrDupW(szExe, ppszApplication);
269
270 if (SUCCEEDED(hr) && ppszCommandLine)
271 {
272 size_t cch = lstrlenW(szProgram) + lstrlenW(pszArgs) + 4; // 4 for '"', '"', ' ', NUL
273 hr = SHCoAlloc(cch * sizeof(WCHAR), (PVOID*)ppszCommandLine);
274 if (SUCCEEDED(hr))
275 hr = StringCchPrintfW(*ppszCommandLine, cch, L"\"%s\" %s", szProgram, pszArgs);
276 }
277
278 if (SUCCEEDED(hr) && ppszParameters)
279 hr = SHStrDupW(pszArgs, ppszParameters);
280
281 if (FAILED(hr))
282 {
283 // Clean up
284 if (*ppszApplication)
285 {
286 CoTaskMemFree(*ppszApplication);
287 *ppszApplication = NULL;
288 }
289 if (ppszCommandLine && *ppszCommandLine)
290 {
291 CoTaskMemFree(*ppszCommandLine);
292 *ppszCommandLine = NULL;
293 }
294 if (ppszParameters && *ppszParameters)
295 {
296 CoTaskMemFree(*ppszParameters);
297 *ppszParameters = NULL;
298 }
299 }
300
301 return hr;
302}
#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
static VOID _MakeAppPathKey(PCWSTR pszPath, PWSTR pszDest, UINT cchDest)
Definition: evalcmd.cpp:110
static HRESULT _PathExeExists(_In_ PCWSTR pszPath)
Definition: evalcmd.cpp:128
static BOOL _PathMatchesSuspicious(PCWSTR lpString)
"Program Files" contains space. It needs special handling. This function will detect it.
Definition: evalcmd.cpp:60
static HRESULT _PathFindInSystem(_Inout_ PWSTR pszPath, _In_ UINT cchPath)
Definition: evalcmd.cpp:158
static PCWSTR _PathGetArgsLikeCreateProcess(PCWSTR lpString)
Definition: evalcmd.cpp:25
static BOOL _GetAppPath(PCWSTR pszPath, PWSTR pszValue, DWORD cchValue)
Definition: evalcmd.cpp:119
static HRESULT _PathCopyExeAndTrimWhiteSpaces(PWSTR pszBuff, size_t cchBuff, PCWSTR pszSrc, size_t cchSrc)
Definition: evalcmd.cpp:49
static PCWSTR _PathGuessNextBestArgs(PCWSTR pszPath)
Definition: evalcmd.cpp:69
static HRESULT _PathFindInFolder(_In_ INT csidl, _In_ PCWSTR pszSrc, _Out_ PWSTR pszPath, _In_ UINT cchPath)
Definition: evalcmd.cpp:144
EXTERN_C HRESULT WINAPI SHEvaluateSystemCommandTemplate(_In_ PCWSTR pszCmdTemplate, _Outptr_ PWSTR *ppszApplication, _Outptr_opt_ PWSTR *ppszCommandLine, _Outptr_opt_ PWSTR *ppszParameters)
Definition: evalcmd.cpp:178
#define PATH_VALID_CHARS
Definition: evalcmd.cpp:18
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
#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
unsigned int UINT
Definition: ndis.h:50
#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:1151
_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:2218
#define CSIDL_SYSTEM
Definition: shlobj.h:2217
#define CSIDL_WINDOWS
Definition: shlobj.h:2216
#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