1 /*
2  * PROJECT:         ReactOS API tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for ShellExecCmdLine
5  * PROGRAMMERS:     Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6  */
7 #include "shelltest.h"
8 #include <shlwapi.h>
9 #include <strsafe.h>
10 #include <versionhelpers.h>
11 #include "shell32_apitest_sub.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 #include <stdio.h>
16 
17 #ifndef SECL_NO_UI
18     #define SECL_NO_UI          0x2
19     #define SECL_LOG_USAGE      0x8
20     #define SECL_USE_IDLIST     0x10
21     #define SECL_ALLOW_NONEXE   0x20
22     #define SECL_RUNAS          0x40
23 #endif
24 
25 #define ShellExecCmdLine proxy_ShellExecCmdLine
26 
27 #define shell32_hInstance   GetModuleHandle(NULL)
28 #define IDS_FILE_NOT_FOUND  (-1)
29 
30 static const WCHAR wszOpen[] = L"open";
31 static const WCHAR wszExe[] = L".exe";
32 static const WCHAR wszCom[] = L".com";
33 
34 static __inline void __SHCloneStrW(WCHAR **target, const WCHAR *source)
35 {
36     *target = (WCHAR *)SHAlloc((lstrlenW(source) + 1) * sizeof(WCHAR) );
37     lstrcpyW(*target, source);
38 }
39 
40 // NOTE: You have to sync the following code to dll/win32/shell32/shlexec.cpp.
41 static LPCWSTR
42 SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0)
43 {
44     LPCWSTR pch;
45     size_t ich = 0;
46     if (*psz == L'"')
47     {
48         // 1st argument is quoted. the string in quotes is quoted 1st argument.
49         // [pch] --> [pszArg0+ich]
50         for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch)
51         {
52             if (*pch == L'"' && pch[1] == L'"')
53             {
54                 // doubled double quotations found!
55                 pszArg0[ich] = L'"';
56             }
57             else if (*pch == L'"')
58             {
59                 // single double quotation found!
60                 ++pch;
61                 break;
62             }
63             else
64             {
65                 // otherwise
66                 pszArg0[ich] = *pch;
67             }
68         }
69     }
70     else
71     {
72         // 1st argument is unquoted. non-space sequence is 1st argument.
73         // [pch] --> [pszArg0+ich]
74         for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch)
75         {
76             pszArg0[ich] = *pch;
77         }
78     }
79     pszArg0[ich] = 0;
80 
81     // skip space
82     while (iswspace(*pch))
83         ++pch;
84 
85     return pch;
86 }
87 
88 HRESULT WINAPI ShellExecCmdLine(
89     HWND hwnd,
90     LPCWSTR pwszCommand,
91     LPCWSTR pwszStartDir,
92     int nShow,
93     LPVOID pUnused,
94     DWORD dwSeclFlags)
95 {
96     SHELLEXECUTEINFOW info;
97     DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC;
98     LPCWSTR pszVerb = NULL;
99     WCHAR szFile[MAX_PATH], szFile2[MAX_PATH];
100     HRESULT hr;
101     LPCWSTR pchParams;
102     LPWSTR lpCommand = NULL;
103 
104     if (pwszCommand == NULL)
105         RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE,
106                        1, (ULONG_PTR*)pwszCommand);
107 
108     __SHCloneStrW(&lpCommand, pwszCommand);
109     StrTrimW(lpCommand, L" \t");
110 
111     if (dwSeclFlags & SECL_NO_UI)
112         dwFlags |= SEE_MASK_FLAG_NO_UI;
113     if (dwSeclFlags & SECL_LOG_USAGE)
114         dwFlags |= SEE_MASK_FLAG_LOG_USAGE;
115     if (dwSeclFlags & SECL_USE_IDLIST)
116         dwFlags |= SEE_MASK_INVOKEIDLIST;
117 
118     if (dwSeclFlags & SECL_RUNAS)
119     {
120         dwSize = 0;
121         hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize);
122         if (SUCCEEDED(hr) && dwSize != 0)
123         {
124             pszVerb = L"runas";
125         }
126     }
127 
128     if (UrlIsFileUrlW(lpCommand))
129     {
130         StringCchCopyW(szFile, _countof(szFile), lpCommand);
131         pchParams = NULL;
132     }
133     else
134     {
135         pchParams = SplitParams(lpCommand, szFile, _countof(szFile));
136         if (szFile[0] != UNICODE_NULL && szFile[1] == L':' &&
137             szFile[2] == UNICODE_NULL)
138         {
139             PathAddBackslashW(szFile);
140         }
141 
142         WCHAR szCurDir[MAX_PATH];
143         GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
144         if (pwszStartDir)
145         {
146             SetCurrentDirectoryW(pwszStartDir);
147         }
148 
149         if (PathIsRelativeW(szFile) &&
150             GetFullPathNameW(szFile, _countof(szFile2), szFile2, NULL) &&
151             PathFileExistsW(szFile2))
152         {
153             StringCchCopyW(szFile, _countof(szFile), szFile2);
154         }
155         else if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
156                  SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
157                  SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) ||
158                  SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
159                  SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
160                  SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL))
161         {
162             StringCchCopyW(szFile, _countof(szFile), szFile2);
163         }
164         else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
165                  SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
166                  SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) ||
167                  SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
168                  SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
169                  SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL))
170         {
171             StringCchCopyW(szFile, _countof(szFile), szFile2);
172             pchParams = NULL;
173         }
174 
175         if (pwszStartDir)
176         {
177             SetCurrentDirectoryW(szCurDir);
178         }
179 
180         if (!(dwSeclFlags & SECL_ALLOW_NONEXE))
181         {
182             if (!GetBinaryTypeW(szFile, &dwType))
183             {
184                 SHFree(lpCommand);
185 
186                 if (!(dwSeclFlags & SECL_NO_UI))
187                 {
188                     WCHAR szText[128 + MAX_PATH], szFormat[128];
189                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
190                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
191                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
192                 }
193                 return CO_E_APPNOTFOUND;
194             }
195         }
196         else
197         {
198             if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
199             {
200                 SHFree(lpCommand);
201 
202                 if (!(dwSeclFlags & SECL_NO_UI))
203                 {
204                     WCHAR szText[128 + MAX_PATH], szFormat[128];
205                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
206                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
207                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
208                 }
209                 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
210             }
211         }
212     }
213 
214     ZeroMemory(&info, sizeof(info));
215     info.cbSize = sizeof(info);
216     info.fMask = dwFlags;
217     info.hwnd = hwnd;
218     info.lpVerb = pszVerb;
219     info.lpFile = szFile;
220     info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL;
221     info.lpDirectory = pwszStartDir;
222     info.nShow = nShow;
223     if (ShellExecuteExW(&info))
224     {
225         if (info.lpIDList)
226             CoTaskMemFree(info.lpIDList);
227 
228         SHFree(lpCommand);
229 
230         return S_OK;
231     }
232 
233     dwError = GetLastError();
234 
235     SHFree(lpCommand);
236 
237     return HRESULT_FROM_WIN32(dwError);
238 }
239 
240 #undef ShellExecCmdLine
241 
242 typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD);
243 SHELLEXECCMDLINE g_pShellExecCmdLine = NULL;
244 
245 typedef struct TEST_ENTRY
246 {
247     INT lineno;
248     BOOL result;
249     BOOL bAllowNonExe;
250     LPCWSTR pwszCommand;
251     LPCWSTR pwszStartDir;
252 } TEST_ENTRY;
253 
254 static WCHAR s_sub_program[MAX_PATH];
255 static WCHAR s_win_test_exe[MAX_PATH];
256 static WCHAR s_sys_bat_file[MAX_PATH];
257 static WCHAR s_cur_dir[MAX_PATH];
258 
259 static BOOL
260 GetSubProgramPath(void)
261 {
262     GetModuleFileNameW(NULL, s_sub_program, _countof(s_sub_program));
263     PathRemoveFileSpecW(s_sub_program);
264     PathAppendW(s_sub_program, L"shell32_apitest_sub.exe");
265 
266     if (!PathFileExistsW(s_sub_program))
267     {
268         PathRemoveFileSpecW(s_sub_program);
269         PathAppendW(s_sub_program, L"testdata\\shell32_apitest_sub.exe");
270 
271         if (!PathFileExistsW(s_sub_program))
272         {
273             return FALSE;
274         }
275     }
276 
277     return TRUE;
278 }
279 
280 static const TEST_ENTRY s_entries_1[] =
281 {
282     // NULL
283     { __LINE__, 0xBADFACE, FALSE, NULL, NULL },
284     { __LINE__, 0xBADFACE, FALSE, NULL, L"." },
285     { __LINE__, 0xBADFACE, FALSE, NULL, L"system32" },
286     { __LINE__, 0xBADFACE, FALSE, NULL, L"C:\\Program Files" },
287     { __LINE__, 0xBADFACE, TRUE, NULL, NULL },
288     { __LINE__, 0xBADFACE, TRUE, NULL, L"." },
289     { __LINE__, 0xBADFACE, TRUE, NULL, L"system32" },
290     { __LINE__, 0xBADFACE, TRUE, NULL, L"C:\\Program Files" },
291     // notepad
292     { __LINE__, TRUE, FALSE, L"notepad", NULL },
293     { __LINE__, TRUE, FALSE, L"notepad", L"." },
294     { __LINE__, TRUE, FALSE, L"notepad", L"system32" },
295     { __LINE__, TRUE, FALSE, L"notepad", L"C:\\Program Files" },
296     { __LINE__, TRUE, FALSE, L"notepad \"Test File.txt\"", NULL },
297     { __LINE__, TRUE, FALSE, L"notepad \"Test File.txt\"", L"." },
298     { __LINE__, TRUE, TRUE, L"notepad", NULL },
299     { __LINE__, TRUE, TRUE, L"notepad", L"." },
300     { __LINE__, TRUE, TRUE, L"notepad", L"system32" },
301     { __LINE__, TRUE, TRUE, L"notepad", L"C:\\Program Files" },
302     { __LINE__, TRUE, TRUE, L"notepad \"Test File.txt\"", NULL },
303     { __LINE__, TRUE, TRUE, L"notepad \"Test File.txt\"", L"." },
304     // notepad.exe
305     { __LINE__, TRUE, FALSE, L"notepad.exe", NULL },
306     { __LINE__, TRUE, FALSE, L"notepad.exe", L"." },
307     { __LINE__, TRUE, FALSE, L"notepad.exe", L"system32" },
308     { __LINE__, TRUE, FALSE, L"notepad.exe", L"C:\\Program Files" },
309     { __LINE__, TRUE, FALSE, L"notepad.exe \"Test File.txt\"", NULL },
310     { __LINE__, TRUE, FALSE, L"notepad.exe \"Test File.txt\"", L"." },
311     { __LINE__, TRUE, TRUE, L"notepad.exe", NULL },
312     { __LINE__, TRUE, TRUE, L"notepad.exe", L"." },
313     { __LINE__, TRUE, TRUE, L"notepad.exe", L"system32" },
314     { __LINE__, TRUE, TRUE, L"notepad.exe", L"C:\\Program Files" },
315     { __LINE__, TRUE, TRUE, L"notepad.exe \"Test File.txt\"", NULL },
316     { __LINE__, TRUE, TRUE, L"notepad.exe \"Test File.txt\"", L"." },
317     // C:\notepad.exe
318     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", NULL },
319     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"." },
320     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"system32" },
321     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe", L"C:\\Program Files" },
322     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe \"Test File.txt\"", NULL },
323     { __LINE__, FALSE, FALSE, L"C:\\notepad.exe \"Test File.txt\"", L"." },
324     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", NULL },
325     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"." },
326     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"system32" },
327     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe", L"C:\\Program Files" },
328     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe \"Test File.txt\"", NULL },
329     { __LINE__, FALSE, TRUE, L"C:\\notepad.exe \"Test File.txt\"", L"." },
330     // "notepad"
331     { __LINE__, TRUE, FALSE, L"\"notepad\"", NULL },
332     { __LINE__, TRUE, FALSE, L"\"notepad\"", L"." },
333     { __LINE__, TRUE, FALSE, L"\"notepad\"", L"system32" },
334     { __LINE__, TRUE, FALSE, L"\"notepad\"", L"C:\\Program Files" },
335     { __LINE__, TRUE, FALSE, L"\"notepad\" \"Test File.txt\"", NULL },
336     { __LINE__, TRUE, FALSE, L"\"notepad\" \"Test File.txt\"", L"." },
337     { __LINE__, TRUE, TRUE, L"\"notepad\"", NULL },
338     { __LINE__, TRUE, TRUE, L"\"notepad\"", L"." },
339     { __LINE__, TRUE, TRUE, L"\"notepad\"", L"system32" },
340     { __LINE__, TRUE, TRUE, L"\"notepad\"", L"C:\\Program Files" },
341     { __LINE__, TRUE, TRUE, L"\"notepad\" \"Test File.txt\"", NULL },
342     { __LINE__, TRUE, TRUE, L"\"notepad\" \"Test File.txt\"", L"." },
343     // "notepad.exe"
344     { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", NULL },
345     { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"." },
346     { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"system32" },
347     { __LINE__, TRUE, FALSE, L"\"notepad.exe\"", L"C:\\Program Files" },
348     { __LINE__, TRUE, FALSE, L"\"notepad.exe\" \"Test File.txt\"", NULL },
349     { __LINE__, TRUE, FALSE, L"\"notepad.exe\" \"Test File.txt\"", L"." },
350     { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", NULL },
351     { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"." },
352     { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"system32" },
353     { __LINE__, TRUE, TRUE, L"\"notepad.exe\"", L"C:\\Program Files" },
354     { __LINE__, TRUE, TRUE, L"\"notepad.exe\" \"Test File.txt\"", NULL },
355     { __LINE__, TRUE, TRUE, L"\"notepad.exe\" \"Test File.txt\"", L"." },
356     // test program
357     { __LINE__, FALSE, FALSE, L"test program", NULL },
358     { __LINE__, FALSE, FALSE, L"test program", L"." },
359     { __LINE__, FALSE, FALSE, L"test program", L"system32" },
360     { __LINE__, FALSE, FALSE, L"test program", L"C:\\Program Files" },
361     { __LINE__, FALSE, FALSE, L"test program \"Test File.txt\"", NULL },
362     { __LINE__, FALSE, FALSE, L"test program \"Test File.txt\"", L"." },
363     { __LINE__, FALSE, TRUE, L"test program", NULL },
364     { __LINE__, FALSE, TRUE, L"test program", L"." },
365     { __LINE__, FALSE, TRUE, L"test program", L"system32" },
366     { __LINE__, FALSE, TRUE, L"test program", L"C:\\Program Files" },
367     { __LINE__, FALSE, TRUE, L"test program \"Test File.txt\"", NULL },
368     { __LINE__, FALSE, TRUE, L"test program \"Test File.txt\"", L"." },
369     // test program.exe
370     { __LINE__, FALSE, FALSE, L"test program.exe", NULL },
371     { __LINE__, FALSE, FALSE, L"test program.exe", L"." },
372     { __LINE__, FALSE, FALSE, L"test program.exe", L"system32" },
373     { __LINE__, FALSE, FALSE, L"test program.exe", L"C:\\Program Files" },
374     { __LINE__, FALSE, FALSE, L"test program.exe \"Test File.txt\"", NULL },
375     { __LINE__, FALSE, FALSE, L"test program.exe \"Test File.txt\"", L"." },
376     { __LINE__, FALSE, TRUE, L"test program.exe", NULL },
377     { __LINE__, FALSE, TRUE, L"test program.exe", L"." },
378     { __LINE__, FALSE, TRUE, L"test program.exe", L"system32" },
379     { __LINE__, FALSE, TRUE, L"test program.exe", L"C:\\Program Files" },
380     { __LINE__, FALSE, TRUE, L"test program.exe \"Test File.txt\"", NULL },
381     { __LINE__, FALSE, TRUE, L"test program.exe \"Test File.txt\"", L"." },
382     // test program.bat
383     { __LINE__, FALSE, FALSE, L"test program.bat", NULL },
384     { __LINE__, FALSE, FALSE, L"test program.bat", L"." },
385     { __LINE__, FALSE, FALSE, L"test program.bat", L"system32" },
386     { __LINE__, FALSE, FALSE, L"test program.bat", L"C:\\Program Files" },
387     { __LINE__, FALSE, FALSE, L"test program.bat \"Test File.txt\"", NULL },
388     { __LINE__, FALSE, FALSE, L"test program.bat \"Test File.txt\"", L"." },
389     { __LINE__, FALSE, TRUE, L"test program.bat", NULL },
390     { __LINE__, FALSE, TRUE, L"test program.bat", L"." },
391     { __LINE__, FALSE, TRUE, L"test program.bat", L"system32" },
392     { __LINE__, FALSE, TRUE, L"test program.bat", L"C:\\Program Files" },
393     { __LINE__, FALSE, TRUE, L"test program.bat \"Test File.txt\"", NULL },
394     { __LINE__, FALSE, TRUE, L"test program.bat \"Test File.txt\"", L"." },
395     // "test program"
396     { __LINE__, FALSE, FALSE, L"\"test program\"", NULL },
397     { __LINE__, FALSE, FALSE, L"\"test program\"", L"." },
398     { __LINE__, FALSE, FALSE, L"\"test program\"", L"system32" },
399     { __LINE__, FALSE, FALSE, L"\"test program\"", L"C:\\Program Files" },
400     { __LINE__, FALSE, FALSE, L"\"test program\" \"Test File.txt\"", NULL },
401     { __LINE__, FALSE, FALSE, L"\"test program\" \"Test File.txt\"", L"." },
402     { __LINE__, FALSE, TRUE, L"\"test program\"", NULL },
403     { __LINE__, FALSE, TRUE, L"\"test program\"", L"." },
404     { __LINE__, FALSE, TRUE, L"\"test program\"", L"system32" },
405     { __LINE__, FALSE, TRUE, L"\"test program\"", L"C:\\Program Files" },
406     { __LINE__, FALSE, TRUE, L"\"test program\" \"Test File.txt\"", NULL },
407     { __LINE__, FALSE, TRUE, L"\"test program\" \"Test File.txt\"", L"." },
408     // "test program.exe"
409     { __LINE__, TRUE, FALSE, L"\"test program.exe\"", NULL },
410     { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"." },
411     { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"system32" },
412     { __LINE__, TRUE, FALSE, L"\"test program.exe\"", L"C:\\Program Files" },
413     { __LINE__, TRUE, FALSE, L"\"test program.exe\" \"Test File.txt\"", NULL },
414     { __LINE__, TRUE, FALSE, L"\"test program.exe\" \"Test File.txt\"", L"." },
415     { __LINE__, TRUE, TRUE, L"\"test program.exe\"", NULL },
416     { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"." },
417     { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"system32" },
418     { __LINE__, TRUE, TRUE, L"\"test program.exe\"", L"C:\\Program Files" },
419     { __LINE__, TRUE, TRUE, L"\"test program.exe\" \"Test File.txt\"", NULL },
420     { __LINE__, TRUE, TRUE, L"\"test program.exe\" \"Test File.txt\"", L"." },
421     // "test program.bat"
422     { __LINE__, FALSE, FALSE, L"\"test program.bat\"", NULL },
423     { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"." },
424     { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"system32" },
425     { __LINE__, FALSE, FALSE, L"\"test program.bat\"", L"C:\\Program Files" },
426     { __LINE__, FALSE, FALSE, L"\"test program.bat\" \"Test File.txt\"", NULL },
427     { __LINE__, FALSE, FALSE, L"\"test program.bat\" \"Test File.txt\"", L"." },
428     { __LINE__, FALSE, TRUE, L"\"test program.bat\"", NULL },
429     { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"." },
430     { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"system32" },
431     { __LINE__, FALSE, TRUE, L"\"test program.bat\"", L"C:\\Program Files" },
432     { __LINE__, FALSE, TRUE, L"\"test program.bat\" \"Test File.txt\"", NULL },
433     { __LINE__, FALSE, TRUE, L"\"test program.bat\" \"Test File.txt\"", L"." },
434     // invalid program
435     { __LINE__, FALSE, FALSE, L"invalid program", NULL },
436     { __LINE__, FALSE, FALSE, L"invalid program", L"." },
437     { __LINE__, FALSE, FALSE, L"invalid program", L"system32" },
438     { __LINE__, FALSE, FALSE, L"invalid program", L"C:\\Program Files" },
439     { __LINE__, FALSE, FALSE, L"invalid program \"Test File.txt\"", NULL },
440     { __LINE__, FALSE, FALSE, L"invalid program \"Test File.txt\"", L"." },
441     { __LINE__, FALSE, TRUE, L"invalid program", NULL },
442     { __LINE__, FALSE, TRUE, L"invalid program", L"." },
443     { __LINE__, FALSE, TRUE, L"invalid program", L"system32" },
444     { __LINE__, FALSE, TRUE, L"invalid program", L"C:\\Program Files" },
445     { __LINE__, FALSE, TRUE, L"invalid program \"Test File.txt\"", NULL },
446     { __LINE__, FALSE, TRUE, L"invalid program \"Test File.txt\"", L"." },
447     // \"invalid program.exe\"
448     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", NULL },
449     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"." },
450     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"system32" },
451     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\"", L"C:\\Program Files" },
452     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\" \"Test File.txt\"", NULL },
453     { __LINE__, FALSE, FALSE, L"\"invalid program.exe\" \"Test File.txt\"", L"." },
454     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", NULL },
455     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"." },
456     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"system32" },
457     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\"", L"C:\\Program Files" },
458     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\" \"Test File.txt\"", NULL },
459     { __LINE__, FALSE, TRUE, L"\"invalid program.exe\" \"Test File.txt\"", L"." },
460     // My Documents
461     { __LINE__, TRUE, TRUE, L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL },
462     { __LINE__, TRUE, TRUE, L"shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL },
463     // shell:sendto
464     { __LINE__, TRUE, TRUE, L"shell:sendto", NULL },
465     // iexplore.exe
466     { __LINE__, TRUE, FALSE, L"iexplore", NULL },
467     { __LINE__, TRUE, FALSE, L"iexplore.exe", NULL },
468     { __LINE__, TRUE, TRUE, L"iexplore", NULL },
469     { __LINE__, TRUE, TRUE, L"iexplore.exe", NULL },
470     // https://google.com
471     { __LINE__, TRUE, FALSE, L"https://google.com", NULL },
472     { __LINE__, TRUE, TRUE, L"https://google.com", NULL },
473     // Test File 1.txt
474     { __LINE__, FALSE, FALSE, L"Test File 1.txt", NULL },
475     { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"." },
476     { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"system32" },
477     { __LINE__, FALSE, FALSE, L"Test File 1.txt", s_cur_dir },
478     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", NULL },
479     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"." },
480     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"system32" },
481     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", s_cur_dir },
482     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL },
483     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." },
484     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" },
485     { __LINE__, FALSE, TRUE, L"Test File 1.txt", NULL },
486     { __LINE__, TRUE, TRUE, L"Test File 1.txt", L"." },
487     { __LINE__, FALSE, TRUE, L"Test File 1.txt", L"system32" },
488     { __LINE__, TRUE, TRUE, L"Test File 1.txt", s_cur_dir },
489     { __LINE__, FALSE, TRUE, L"\"Test File 1.txt\"", NULL },
490     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"." },
491     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"system32" },
492     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", s_cur_dir },
493     { __LINE__, FALSE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL },
494     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." },
495     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" },
496     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir },
497     // Test File 2.bat
498     { __LINE__, FALSE, FALSE, L"Test File 2.bat", NULL },
499     { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"." },
500     { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"system32" },
501     { __LINE__, FALSE, FALSE, L"Test File 2.bat", s_cur_dir },
502     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", NULL },
503     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"." },
504     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"system32" },
505     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", s_cur_dir },
506     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL },
507     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." },
508     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" },
509     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir },
510     { __LINE__, FALSE, TRUE, L"Test File 2.bat", NULL },
511     { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"." },
512     { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"system32" },
513     { __LINE__, FALSE, TRUE, L"Test File 2.bat", s_cur_dir },
514     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", NULL },
515     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"." },
516     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"system32" },
517     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", s_cur_dir },
518     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL },
519     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." },
520     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" },
521     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir },
522 };
523 
524 static const TEST_ENTRY s_entries_2[] =
525 {
526     // Test File 1.txt (with setting path)
527     { __LINE__, FALSE, FALSE, L"Test File 1.txt", NULL },
528     { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"." },
529     { __LINE__, FALSE, FALSE, L"Test File 1.txt", L"system32" },
530     { __LINE__, FALSE, FALSE, L"Test File 1.txt", s_cur_dir },
531     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", NULL },
532     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"." },
533     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", L"system32" },
534     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\"", s_cur_dir },
535     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL },
536     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." },
537     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" },
538     { __LINE__, FALSE, FALSE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir },
539     { __LINE__, FALSE, TRUE, L"Test File 1.txt", NULL },
540     { __LINE__, TRUE, TRUE, L"Test File 1.txt", L"." },
541     { __LINE__, FALSE, TRUE, L"Test File 1.txt", L"system32" },
542     { __LINE__, TRUE, TRUE, L"Test File 1.txt", s_cur_dir },
543     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", NULL },
544     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"." },
545     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", L"system32" },
546     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\"", s_cur_dir },
547     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", NULL },
548     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"." },
549     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", L"system32" },
550     { __LINE__, TRUE, TRUE, L"\"Test File 1.txt\" \"Test File.txt\"", s_cur_dir },
551     // Test File 2.bat (with setting path)
552     { __LINE__, FALSE, FALSE, L"Test File 2.bat", NULL },
553     { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"." },
554     { __LINE__, FALSE, FALSE, L"Test File 2.bat", L"system32" },
555     { __LINE__, FALSE, FALSE, L"Test File 2.bat", s_cur_dir },
556     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", NULL },
557     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"." },
558     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", L"system32" },
559     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\"", s_cur_dir },
560     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL },
561     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." },
562     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" },
563     { __LINE__, FALSE, FALSE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir },
564     { __LINE__, FALSE, TRUE, L"Test File 2.bat", NULL },
565     { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"." },
566     { __LINE__, FALSE, TRUE, L"Test File 2.bat", L"system32" },
567     { __LINE__, FALSE, TRUE, L"Test File 2.bat", s_cur_dir },
568     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", NULL },
569     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"." },
570     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", L"system32" },
571     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\"", s_cur_dir },
572     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", NULL },
573     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"." },
574     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", L"system32" },
575     { __LINE__, FALSE, TRUE, L"\"Test File 2.bat\" \"Test File.txt\"", s_cur_dir },
576 };
577 
578 typedef struct OPENWNDS
579 {
580     UINT count;
581     HWND *phwnd;
582 } OPENWNDS;
583 
584 static OPENWNDS s_wi0 = { 0 }, s_wi1 = { 0 };
585 
586 static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
587 {
588     OPENWNDS *info = (OPENWNDS *)lParam;
589     info->phwnd = (HWND *)realloc(info->phwnd, (info->count + 1) * sizeof(HWND));
590     if (!info->phwnd)
591         return FALSE;
592     info->phwnd[info->count] = hwnd;
593     ++(info->count);
594     return TRUE;
595 }
596 
597 static void CleanupNewlyCreatedWindows(void)
598 {
599     EnumWindows(EnumWindowsProc, (LPARAM)&s_wi1);
600     for (UINT i1 = 0; i1 < s_wi1.count; ++i1)
601     {
602         BOOL bFound = FALSE;
603         for (UINT i0 = 0; i0 < s_wi0.count; ++i0)
604         {
605             if (s_wi1.phwnd[i1] == s_wi0.phwnd[i0])
606             {
607                 bFound = TRUE;
608                 break;
609             }
610         }
611         if (!bFound)
612         {
613             DWORD dwPID;
614             GetWindowThreadProcessId(s_wi1.phwnd[i1], &dwPID);
615             HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, TRUE, dwPID);
616             TerminateProcess(hProcess, -1);
617             CloseHandle(hProcess);
618         }
619     }
620     free(s_wi1.phwnd);
621     ZeroMemory(&s_wi1, sizeof(s_wi1));
622 }
623 
624 static void DoEntry(const TEST_ENTRY *pEntry)
625 {
626     HRESULT hr;
627     DWORD dwSeclFlags;
628     BOOL result;
629 
630     if (pEntry->bAllowNonExe)
631         dwSeclFlags = SECL_NO_UI | SECL_ALLOW_NONEXE;
632     else
633         dwSeclFlags = SECL_NO_UI;
634 
635     _SEH2_TRY
636     {
637         if (IsReactOS())
638         {
639             hr = proxy_ShellExecCmdLine(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
640                                         SW_SHOWNORMAL, NULL, dwSeclFlags);
641         }
642         else
643         {
644             hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
645                                         SW_SHOWNORMAL, NULL, dwSeclFlags);
646         }
647     }
648     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
649     {
650         hr = 0xBADFACE;
651     }
652     _SEH2_END;
653 
654     if (hr == 0xBADFACE)
655         result = hr;
656     else
657         result = (hr == S_OK);
658 
659     ok(result == pEntry->result, "Line %d: result expected %d, was %d\n",
660        pEntry->lineno, pEntry->result, result);
661 
662     CleanupNewlyCreatedWindows();
663 }
664 
665 START_TEST(ShellExecCmdLine)
666 {
667     using namespace std;
668 
669     if (!IsReactOS())
670     {
671         if (!IsWindowsVistaOrGreater())
672         {
673             skip("ShellExecCmdLine is not available on this platform\n");
674             return;
675         }
676 
677         HMODULE hShell32 = GetModuleHandleA("shell32");
678         g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265);
679         if (!g_pShellExecCmdLine)
680         {
681             skip("ShellExecCmdLine is not found\n");
682             return;
683         }
684     }
685 
686     if (!GetSubProgramPath())
687     {
688         skip("shell32_apitest_sub.exe is not found\n");
689         return;
690     }
691 
692     // record open windows
693     if (!EnumWindows(EnumWindowsProc, (LPARAM)&s_wi0))
694     {
695         skip("EnumWindows failed\n");
696         free(s_wi0.phwnd);
697         return;
698     }
699 
700     // s_win_test_exe
701     GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
702     PathAppendW(s_win_test_exe, L"test program.exe");
703     BOOL ret = CopyFileW(s_sub_program, s_win_test_exe, FALSE);
704     if (!ret)
705     {
706         skip("Please retry with admin rights\n");
707         free(s_wi0.phwnd);
708         return;
709     }
710 
711     FILE *fp;
712 
713     // s_sys_bat_file
714     GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
715     PathAppendW(s_sys_bat_file, L"test program.bat");
716     fp = _wfopen(s_sys_bat_file, L"wb");
717     fclose(fp);
718     ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
719 
720     // "Test File 1.txt"
721     fp = fopen("Test File 1.txt", "wb");
722     ok(fp != NULL, "failed to create a test file\n");
723     fclose(fp);
724     ok_int(PathFileExistsA("Test File 1.txt"), TRUE);
725 
726     // "Test File 2.bat"
727     fp = fopen("Test File 2.bat", "wb");
728     ok(fp != NULL, "failed to create a test file\n");
729     fclose(fp);
730     ok_int(PathFileExistsA("Test File 2.bat"), TRUE);
731 
732     // s_cur_dir
733     GetCurrentDirectoryW(_countof(s_cur_dir), s_cur_dir);
734 
735     // do tests
736     for (size_t i = 0; i < _countof(s_entries_1); ++i)
737     {
738         DoEntry(&s_entries_1[i]);
739     }
740     SetEnvironmentVariableW(L"PATH", s_cur_dir);
741     for (size_t i = 0; i < _countof(s_entries_2); ++i)
742     {
743         DoEntry(&s_entries_2[i]);
744     }
745 
746     Sleep(2000);
747     CleanupNewlyCreatedWindows();
748 
749     // clean up
750     ok(DeleteFileW(s_win_test_exe), "failed to delete the test file\n");
751     ok(DeleteFileW(s_sys_bat_file), "failed to delete the test file\n");
752     ok(DeleteFileA("Test File 1.txt"), "failed to delete the test file\n");
753     ok(DeleteFileA("Test File 2.bat"), "failed to delete the test file\n");
754     free(s_wi0.phwnd);
755 
756     DoWaitForWindow(CLASSNAME, CLASSNAME, TRUE, TRUE);
757     Sleep(100);
758 }
759