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 
12 #define NDEBUG
13 #include <debug.h>
14 #include <stdio.h>
15 
16 #ifndef SECL_NO_UI
17     #define SECL_NO_UI          0x2
18     #define SECL_LOG_USAGE      0x8
19     #define SECL_USE_IDLIST     0x10
20     #define SECL_ALLOW_NONEXE   0x20
21     #define SECL_RUNAS          0x40
22 #endif
23 
24 #define ShellExecCmdLine proxy_ShellExecCmdLine
25 
26 #define shell32_hInstance   GetModuleHandle(NULL)
27 #define IDS_FILE_NOT_FOUND  (-1)
28 
29 static const WCHAR wszOpen[] = L"open";
30 static const WCHAR wszExe[] = L".exe";
31 static const WCHAR wszCom[] = L".com";
32 
33 static __inline void __SHCloneStrW(WCHAR **target, const WCHAR *source)
34 {
35 	*target = (WCHAR *)SHAlloc((lstrlenW(source) + 1) * sizeof(WCHAR) );
36 	lstrcpyW(*target, source);
37 }
38 
39 // NOTE: You have to sync the following code to dll/win32/shell32/shlexec.cpp.
40 static LPCWSTR
41 SplitParams(LPCWSTR psz, LPWSTR pszArg0, size_t cchArg0)
42 {
43     LPCWSTR pch;
44     size_t ich = 0;
45     if (*psz == L'"')
46     {
47         // 1st argument is quoted. the string in quotes is quoted 1st argument.
48         // [pch] --> [pszArg0+ich]
49         for (pch = psz + 1; *pch && ich + 1 < cchArg0; ++ich, ++pch)
50         {
51             if (*pch == L'"' && pch[1] == L'"')
52             {
53                 // doubled double quotations found!
54                 pszArg0[ich] = L'"';
55             }
56             else if (*pch == L'"')
57             {
58                 // single double quotation found!
59                 ++pch;
60                 break;
61             }
62             else
63             {
64                 // otherwise
65                 pszArg0[ich] = *pch;
66             }
67         }
68     }
69     else
70     {
71         // 1st argument is unquoted. non-space sequence is 1st argument.
72         // [pch] --> [pszArg0+ich]
73         for (pch = psz; *pch && !iswspace(*pch) && ich + 1 < cchArg0; ++ich, ++pch)
74         {
75             pszArg0[ich] = *pch;
76         }
77     }
78     pszArg0[ich] = 0;
79 
80     // skip space
81     while (iswspace(*pch))
82         ++pch;
83 
84     return pch;
85 }
86 
87 HRESULT WINAPI ShellExecCmdLine(
88     HWND hwnd,
89     LPCWSTR pwszCommand,
90     LPCWSTR pwszStartDir,
91     int nShow,
92     LPVOID pUnused,
93     DWORD dwSeclFlags)
94 {
95     SHELLEXECUTEINFOW info;
96     DWORD dwSize, dwError, dwType, dwFlags = SEE_MASK_DOENVSUBST | SEE_MASK_NOASYNC;
97     LPCWSTR pszVerb = NULL;
98     WCHAR szFile[MAX_PATH], szFile2[MAX_PATH];
99     HRESULT hr;
100     LPCWSTR pchParams;
101     LPWSTR lpCommand = NULL;
102 
103     if (pwszCommand == NULL)
104         RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE,
105                        1, (ULONG_PTR*)pwszCommand);
106 
107     __SHCloneStrW(&lpCommand, pwszCommand);
108     StrTrimW(lpCommand, L" \t");
109 
110     if (dwSeclFlags & SECL_NO_UI)
111         dwFlags |= SEE_MASK_FLAG_NO_UI;
112     if (dwSeclFlags & SECL_LOG_USAGE)
113         dwFlags |= SEE_MASK_FLAG_LOG_USAGE;
114     if (dwSeclFlags & SECL_USE_IDLIST)
115         dwFlags |= SEE_MASK_INVOKEIDLIST;
116 
117     if (dwSeclFlags & SECL_RUNAS)
118     {
119         dwSize = 0;
120         hr = AssocQueryStringW(0, ASSOCSTR_COMMAND, lpCommand, L"RunAs", NULL, &dwSize);
121         if (SUCCEEDED(hr) && dwSize != 0)
122         {
123             pszVerb = L"runas";
124         }
125     }
126 
127     if (UrlIsFileUrlW(lpCommand))
128     {
129         StringCchCopyW(szFile, _countof(szFile), lpCommand);
130         pchParams = NULL;
131     }
132     else
133     {
134         pchParams = SplitParams(lpCommand, szFile, _countof(szFile));
135         if (szFile[0] != UNICODE_NULL && szFile[1] == L':' &&
136             szFile[2] == UNICODE_NULL)
137         {
138             PathAddBackslashW(szFile);
139         }
140 
141         WCHAR szCurDir[MAX_PATH];
142         GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
143         if (pwszStartDir)
144         {
145             SetCurrentDirectoryW(pwszStartDir);
146         }
147 
148         if (PathIsRelativeW(szFile) &&
149             GetFullPathNameW(szFile, _countof(szFile2), szFile2, NULL) &&
150             PathFileExistsW(szFile2))
151         {
152             StringCchCopyW(szFile, _countof(szFile), szFile2);
153         }
154         else if (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
155                  SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
156                  SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) ||
157                  SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
158                  SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
159                  SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL))
160         {
161             StringCchCopyW(szFile, _countof(szFile), szFile2);
162         }
163         else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
164                  SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
165                  SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) ||
166                  SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
167                  SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
168                  SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL))
169         {
170             StringCchCopyW(szFile, _countof(szFile), szFile2);
171             pchParams = NULL;
172         }
173 
174         if (pwszStartDir)
175         {
176             SetCurrentDirectoryW(szCurDir);
177         }
178 
179         if (!(dwSeclFlags & SECL_ALLOW_NONEXE))
180         {
181             if (!GetBinaryTypeW(szFile, &dwType))
182             {
183                 SHFree(lpCommand);
184 
185                 if (!(dwSeclFlags & SECL_NO_UI))
186                 {
187                     WCHAR szText[128 + MAX_PATH], szFormat[128];
188                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
189                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
190                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
191                 }
192                 return CO_E_APPNOTFOUND;
193             }
194         }
195         else
196         {
197             if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
198             {
199                 SHFree(lpCommand);
200 
201                 if (!(dwSeclFlags & SECL_NO_UI))
202                 {
203                     WCHAR szText[128 + MAX_PATH], szFormat[128];
204                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
205                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
206                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
207                 }
208                 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
209             }
210         }
211     }
212 
213     ZeroMemory(&info, sizeof(info));
214     info.cbSize = sizeof(info);
215     info.fMask = dwFlags;
216     info.hwnd = hwnd;
217     info.lpVerb = pszVerb;
218     info.lpFile = szFile;
219     info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL;
220     info.lpDirectory = pwszStartDir;
221     info.nShow = nShow;
222     if (ShellExecuteExW(&info))
223     {
224         if (info.lpIDList)
225             CoTaskMemFree(info.lpIDList);
226 
227         SHFree(lpCommand);
228 
229         return S_OK;
230     }
231 
232     dwError = GetLastError();
233 
234     SHFree(lpCommand);
235 
236     return HRESULT_FROM_WIN32(dwError);
237 }
238 
239 #undef ShellExecCmdLine
240 
241 typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD);
242 SHELLEXECCMDLINE g_pShellExecCmdLine = NULL;
243 
244 typedef struct TEST_ENTRY
245 {
246     INT lineno;
247     HRESULT hr;
248     BOOL bAllowNonExe;
249     LPCWSTR pwszWindowClass;
250     LPCWSTR pwszCommand;
251     LPCWSTR pwszStartDir;
252 } TEST_ENTRY;
253 
254 static const char s_testfile1[] = "Test File.txt";
255 static const char s_testfile2[] = "Test File.bat";
256 static char s_notepad[] = "notepad.exe";
257 
258 static const TEST_ENTRY s_entries[] =
259 {
260     // NULL
261     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, NULL },
262     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"." },
263     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"system32" },
264     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"C:\\Program Files" },
265     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, NULL },
266     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"." },
267     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"system32" },
268     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"C:\\Program Files" },
269     // notepad
270     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", NULL },
271     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"." },
272     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"system32" },
273     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"C:\\Program Files" },
274     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
275     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
276     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", NULL },
277     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"." },
278     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"system32" },
279     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"C:\\Program Files" },
280     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
281     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
282     // notepad.exe
283     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", NULL },
284     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"." },
285     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"system32" },
286     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
287     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
288     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
289     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", NULL },
290     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"." },
291     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"system32" },
292     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
293     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
294     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
295     // C:\notepad.exe
296     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", NULL },
297     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"." },
298     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"system32" },
299     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
300     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
301     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
302     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", NULL },
303     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"." },
304     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"system32" },
305     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
306     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
307     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
308     // "notepad"
309     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", NULL },
310     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"." },
311     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"system32" },
312     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
313     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
314     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
315     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", NULL },
316     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"." },
317     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"system32" },
318     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
319     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
320     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
321     // "notepad.exe"
322     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", NULL },
323     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"." },
324     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"system32" },
325     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
326     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
327     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
328     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", NULL },
329     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"." },
330     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"system32" },
331     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
332     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
333     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
334     // test program.exe
335     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", NULL },
336     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"." },
337     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"system32" },
338     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
339     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
340     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
341     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", NULL },
342     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"." },
343     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"system32" },
344     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
345     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
346     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
347     // "test program"
348     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", NULL },
349     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"." },
350     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"system32" },
351     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
352     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
353     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
354     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", NULL },
355     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"." },
356     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"system32" },
357     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
358     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
359     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
360     // "test program.exe"
361     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", NULL },
362     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"." },
363     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"system32" },
364     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
365     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
366     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
367     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", NULL },
368     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"." },
369     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"system32" },
370     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
371     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
372     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
373     // invalid program
374     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", NULL },
375     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"." },
376     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"system32" },
377     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"C:\\Program Files" },
378     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
379     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
380     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", NULL },
381     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"." },
382     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"system32" },
383     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"C:\\Program Files" },
384     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
385     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
386     // \"invalid program.exe\"
387     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", NULL },
388     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"." },
389     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
390     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
391     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
392     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
393     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", NULL },
394     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"." },
395     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
396     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
397     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
398     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
399     // My Documents
400     { __LINE__, S_OK, TRUE, NULL, L"::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL },
401     { __LINE__, S_OK, TRUE, NULL, L"shell:::{450d8fba-ad25-11d0-98a8-0800361b1103}", NULL },
402     // Control Panel
403     { __LINE__, S_OK, TRUE, NULL, L"::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}", NULL },
404     { __LINE__, S_OK, TRUE, NULL, L"shell:::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}", NULL },
405 };
406 
407 static void DoEntry(const TEST_ENTRY *pEntry)
408 {
409     HRESULT hr;
410     DWORD dwSeclFlags;
411 
412     if (pEntry->bAllowNonExe)
413         dwSeclFlags = SECL_NO_UI | SECL_ALLOW_NONEXE;
414     else
415         dwSeclFlags = SECL_NO_UI;
416 
417     _SEH2_TRY
418     {
419         if (IsReactOS())
420         {
421             hr = proxy_ShellExecCmdLine(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
422                                         SW_SHOWNORMAL, NULL, dwSeclFlags);
423         }
424         else
425         {
426             hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
427                                         SW_SHOWNORMAL, NULL, dwSeclFlags);
428         }
429     }
430     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
431     {
432         hr = 0xDEADFACE;
433     }
434     _SEH2_END;
435 
436     ok(hr == pEntry->hr, "Line %d: hr expected 0x%lX, was 0x%lX\n", pEntry->lineno, pEntry->hr, hr);
437 
438 #define RETRY_COUNT     5
439 #define RETRY_INTERVAL  250
440     if (SUCCEEDED(hr) && pEntry->pwszWindowClass)
441     {
442         BOOL bFound = FALSE;
443         Sleep(RETRY_INTERVAL / 2);
444         for (int i = 0; i < RETRY_COUNT; ++i)
445         {
446             HWND hwnd = FindWindowW(pEntry->pwszWindowClass, NULL);
447             if (hwnd)
448             {
449                 bFound = TRUE;
450                 SendMessage(hwnd, WM_CLOSE, 0, 0);
451                 Sleep(RETRY_INTERVAL);
452                 break;
453             }
454             Sleep(RETRY_INTERVAL);
455         }
456         ok(bFound, "Line %d: The window not found\n", pEntry->lineno);
457     }
458 #undef RETRY_COUNT
459 #undef RETRY_INTERVAL
460 }
461 
462 START_TEST(ShellExecCmdLine)
463 {
464     using namespace std;
465 
466     if (!IsReactOS())
467     {
468         if (!IsWindowsVistaOrGreater())
469         {
470             skip("ShellExecCmdLine is not available on this platform\n");
471             return;
472         }
473 
474         HMODULE hShell32 = GetModuleHandleA("shell32");
475         g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265);
476         if (!g_pShellExecCmdLine)
477         {
478             skip("ShellExecCmdLine is not found\n");
479             return;
480         }
481     }
482 
483     // s_testfile1
484     FILE *fp = fopen(s_testfile1, "wb");
485     ok(fp != NULL, "failed to create a test file\n");
486     fclose(fp);
487 
488     // s_testfile2
489     fp = fopen(s_testfile2, "wb");
490     ok(fp != NULL, "failed to create a test file\n");
491     if (fp)
492     {
493         fprintf(fp, "echo OK\n");
494     }
495     fclose(fp);
496 
497     for (size_t i = 0; i < _countof(s_entries); ++i)
498     {
499         DoEntry(&s_entries[i]);
500     }
501 
502     WCHAR buf0[MAX_PATH];
503     WCHAR buf1[MAX_PATH];
504     WCHAR buf2[MAX_PATH];
505     TEST_ENTRY additionals[] =
506     {
507         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, NULL },
508         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"." },
509         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"system32" },
510         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, NULL },
511         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"." },
512         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"system32" },
513         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, NULL },
514         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"." },
515         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"system32" },
516         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, NULL }, // FIXME
517         { __LINE__, S_OK, TRUE, L"Notepad", buf0, L"." },
518         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, L"system32" }, // FIXME
519         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf1, NULL }, // FIXME
520         { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"." },
521         { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"system32" },
522         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf2, NULL }, // FIXME
523         { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"." },
524         { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"system32" },
525     };
526 
527     wsprintfW(buf0, L"%hs", s_testfile1);
528     wsprintfW(buf1, L"\"%hs\"", s_testfile1);
529     wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile1);
530     for (size_t i = 0; i < _countof(additionals); ++i)
531     {
532         DoEntry(&additionals[i]);
533     }
534 
535     TEST_ENTRY additionals2[] =
536     {
537         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, NULL },
538         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"." },
539         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"system32" },
540         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, NULL },
541         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"." },
542         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"system32" },
543         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, NULL },
544         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"." },
545         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"system32" },
546         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, NULL }, // FIXME
547         { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
548         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, L"system32" },  // FIXME
549         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf1, NULL }, // FIXME
550         { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
551         { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
552         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf2, NULL }, // FIXME
553         { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
554         { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
555     };
556 
557     wsprintfW(buf0, L"%hs", s_testfile2);
558     wsprintfW(buf1, L"\"%hs\"", s_testfile2);
559     wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile2);
560     for (size_t i = 0; i < _countof(additionals2); ++i)
561     {
562         DoEntry(&additionals2[i]);
563     }
564 
565     char path[MAX_PATH];
566     ok((INT_PTR)FindExecutableA("notepad.exe", NULL, s_notepad) >= 32, "FindExecutableA failed\n");
567     ok(GetModuleFileNameA(NULL, path, _countof(path)), "GetModuleFileNameA failed\n");
568     char *pch = strrchr(path, '\\');
569 
570     if (pch == NULL)
571     {
572         skip("pch == NULL\n");
573     }
574     else
575     {
576         // create "My Directory"
577         strcpy(pch, "\\My Directory");
578         if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES)
579             ok(CreateDirectoryA(path, NULL), "CreateDirectoryA failed\n");
580 
581         // create "My Directory\\Notepad.exe" as clone of Notepad.exe
582         strcpy(pch, "\\My Directory\\Notepad.exe");
583         ok(CopyFileA(s_notepad, path, FALSE), "CopyFileA failed\n");
584 
585         wsprintfW(buf0, L"%hs", path);
586         wsprintfW(buf1, L"\"%hs\"", path);
587         wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", path);
588         TEST_ENTRY additionals3[] =
589         {
590             { __LINE__, S_OK, FALSE, NULL, buf0, NULL },
591             { __LINE__, S_OK, FALSE, NULL, buf0, L"." },
592             { __LINE__, S_OK, FALSE, NULL, buf0, L"system32" },
593             { __LINE__, S_OK, FALSE, NULL, buf1, NULL },
594             { __LINE__, S_OK, FALSE, NULL, buf1, L"." },
595             { __LINE__, S_OK, FALSE, NULL, buf1, L"system32" },
596             { __LINE__, S_OK, FALSE, NULL, buf2, NULL },
597             { __LINE__, S_OK, FALSE, NULL, buf2, L"." },
598             { __LINE__, S_OK, FALSE, NULL, buf2, L"system32" },
599             { __LINE__, S_OK, TRUE, NULL, buf0, NULL },
600             { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
601             { __LINE__, S_OK, TRUE, NULL, buf0, L"system32" },
602             { __LINE__, S_OK, TRUE, NULL, buf1, NULL },
603             { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
604             { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
605             { __LINE__, S_OK, TRUE, NULL, buf2, NULL },
606             { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
607             { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
608         };
609         for (size_t i = 0; i < _countof(additionals3); ++i)
610         {
611             DoEntry(&additionals3[i]);
612         }
613 
614         DeleteFileA(path);
615 
616         strcpy(pch, "\\My Directory");
617         RemoveDirectory(path);
618     }
619 
620     // clean up
621     ok(DeleteFileA(s_testfile1), "failed to delete the test file\n");
622     ok(DeleteFileA(s_testfile2), "failed to delete the test file\n");
623 }
624