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 
11 #define NDEBUG
12 #include <debug.h>
13 #include <stdio.h>
14 
15 //#define ShellExecCmdLine ShellExecCmdLine
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 #ifdef 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 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 (SearchPathW(NULL, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
136             SearchPathW(NULL, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
137             SearchPathW(NULL, szFile, wszCom, _countof(szFile2), szFile2, NULL) ||
138             SearchPathW(pwszStartDir, szFile, NULL, _countof(szFile2), szFile2, NULL) ||
139             SearchPathW(pwszStartDir, szFile, wszExe, _countof(szFile2), szFile2, NULL) ||
140             SearchPathW(pwszStartDir, szFile, wszCom, _countof(szFile2), szFile2, NULL))
141         {
142             StringCchCopyW(szFile, _countof(szFile), szFile2);
143         }
144         else if (SearchPathW(NULL, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
145                  SearchPathW(NULL, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
146                  SearchPathW(NULL, lpCommand, wszCom, _countof(szFile2), szFile2, NULL) ||
147                  SearchPathW(pwszStartDir, lpCommand, NULL, _countof(szFile2), szFile2, NULL) ||
148                  SearchPathW(pwszStartDir, lpCommand, wszExe, _countof(szFile2), szFile2, NULL) ||
149                  SearchPathW(pwszStartDir, lpCommand, wszCom, _countof(szFile2), szFile2, NULL))
150         {
151             StringCchCopyW(szFile, _countof(szFile), szFile2);
152             pchParams = NULL;
153         }
154 
155         if (!(dwSeclFlags & SECL_ALLOW_NONEXE))
156         {
157             if (!GetBinaryTypeW(szFile, &dwType))
158             {
159                 SHFree(lpCommand);
160 
161                 if (!(dwSeclFlags & SECL_NO_UI))
162                 {
163                     WCHAR szText[128 + MAX_PATH], szFormat[128];
164                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
165                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
166                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
167                 }
168                 return CO_E_APPNOTFOUND;
169             }
170         }
171         else
172         {
173             if (GetFileAttributesW(szFile) == INVALID_FILE_ATTRIBUTES)
174             {
175                 SHFree(lpCommand);
176 
177                 if (!(dwSeclFlags & SECL_NO_UI))
178                 {
179                     WCHAR szText[128 + MAX_PATH], szFormat[128];
180                     LoadStringW(shell32_hInstance, IDS_FILE_NOT_FOUND, szFormat, _countof(szFormat));
181                     StringCchPrintfW(szText, _countof(szText), szFormat, szFile);
182                     MessageBoxW(hwnd, szText, NULL, MB_ICONERROR);
183                 }
184                 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
185             }
186         }
187     }
188 
189     ZeroMemory(&info, sizeof(info));
190     info.cbSize = sizeof(info);
191     info.fMask = dwFlags;
192     info.hwnd = hwnd;
193     info.lpVerb = pszVerb;
194     info.lpFile = szFile;
195     info.lpParameters = (pchParams && *pchParams) ? pchParams : NULL;
196     info.lpDirectory = pwszStartDir;
197     info.nShow = nShow;
198     if (ShellExecuteExW(&info))
199     {
200         if (info.lpIDList)
201             CoTaskMemFree(info.lpIDList);
202 
203         SHFree(lpCommand);
204 
205         return S_OK;
206     }
207 
208     dwError = GetLastError();
209 
210     SHFree(lpCommand);
211 
212     return HRESULT_FROM_WIN32(dwError);
213 }
214 #else
215     typedef HRESULT (WINAPI *SHELLEXECCMDLINE)(HWND, LPCWSTR, LPCWSTR, INT, LPVOID, DWORD);
216     SHELLEXECCMDLINE g_pShellExecCmdLine = NULL;
217 #endif
218 
219 typedef struct TEST_ENTRY
220 {
221     INT lineno;
222     HRESULT hr;
223     BOOL bAllowNonExe;
224     LPCWSTR pwszWindowClass;
225     LPCWSTR pwszCommand;
226     LPCWSTR pwszStartDir;
227 } TEST_ENTRY;
228 
229 static const char s_testfile1[] = "Test File.txt";
230 static const char s_testfile2[] = "Test File.bat";
231 static char s_notepad[] = "notepad.exe";
232 
233 static const TEST_ENTRY s_entries[] =
234 {
235     // NULL
236     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, NULL },
237     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"." },
238     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"system32" },
239     { __LINE__, (HRESULT)0xDEADFACE, FALSE, NULL, NULL, L"C:\\Program Files" },
240     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, NULL },
241     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"." },
242     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"system32" },
243     { __LINE__, (HRESULT)0xDEADFACE, TRUE, NULL, NULL, L"C:\\Program Files" },
244     // notepad
245     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", NULL },
246     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"." },
247     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"system32" },
248     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad", L"C:\\Program Files" },
249     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
250     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
251     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", NULL },
252     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"." },
253     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"system32" },
254     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad", L"C:\\Program Files" },
255     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", NULL },
256     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad \"Test File.txt\"", L"." },
257     // notepad.exe
258     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", NULL },
259     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"." },
260     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"system32" },
261     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
262     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
263     { __LINE__, S_OK, FALSE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
264     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", NULL },
265     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"." },
266     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"system32" },
267     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe", L"C:\\Program Files" },
268     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", NULL },
269     { __LINE__, S_OK, TRUE, L"Notepad", L"notepad.exe \"Test File.txt\"", L"." },
270     // C:\notepad.exe
271     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", NULL },
272     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"." },
273     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"system32" },
274     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
275     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
276     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
277     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", NULL },
278     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"." },
279     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"system32" },
280     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe", L"C:\\Program Files" },
281     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", NULL },
282     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"C:\\notepad.exe \"Test File.txt\"", L"." },
283     // "notepad"
284     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", NULL },
285     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"." },
286     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"system32" },
287     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
288     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
289     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
290     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", NULL },
291     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"." },
292     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"system32" },
293     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\"", L"C:\\Program Files" },
294     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", NULL },
295     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad\" \"Test File.txt\"", L"." },
296     // "notepad.exe"
297     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", NULL },
298     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"." },
299     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"system32" },
300     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
301     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
302     { __LINE__, S_OK, FALSE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
303     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", NULL },
304     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"." },
305     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"system32" },
306     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\"", L"C:\\Program Files" },
307     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", NULL },
308     { __LINE__, S_OK, TRUE, L"Notepad", L"\"notepad.exe\" \"Test File.txt\"", L"." },
309     // test program.exe
310     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", NULL },
311     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"." },
312     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"system32" },
313     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
314     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
315     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
316     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", NULL },
317     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"." },
318     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"system32" },
319     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe", L"C:\\Program Files" },
320     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", NULL },
321     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"test program.exe \"Test File.txt\"", L"." },
322     // "test program"
323     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", NULL },
324     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"." },
325     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"system32" },
326     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
327     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
328     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
329     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", NULL },
330     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"." },
331     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"system32" },
332     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\"", L"C:\\Program Files" },
333     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", NULL },
334     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program\" \"Test File.txt\"", L"." },
335     // "test program.exe"
336     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", NULL },
337     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"." },
338     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"system32" },
339     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
340     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
341     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
342     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", NULL },
343     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"." },
344     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"system32" },
345     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\"", L"C:\\Program Files" },
346     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", NULL },
347     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"test program.exe\" \"Test File.txt\"", L"." },
348     // invalid program
349     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", NULL },
350     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"." },
351     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"system32" },
352     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program", L"C:\\Program Files" },
353     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
354     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
355     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", NULL },
356     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"." },
357     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"system32" },
358     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program", L"C:\\Program Files" },
359     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", NULL },
360     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"invalid program \"Test File.txt\"", L"." },
361     // \"invalid program.exe\"
362     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", NULL },
363     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"." },
364     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
365     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
366     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
367     { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
368     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", NULL },
369     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"." },
370     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"system32" },
371     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\"", L"C:\\Program Files" },
372     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", NULL },
373     { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", L"\"invalid program.exe\" \"Test File.txt\"", L"." },
374 };
375 
376 static void DoEntry(const TEST_ENTRY *pEntry)
377 {
378     HRESULT hr;
379     DWORD dwSeclFlags;
380 
381     if (pEntry->bAllowNonExe)
382         dwSeclFlags = SECL_NO_UI | SECL_ALLOW_NONEXE;
383     else
384         dwSeclFlags = SECL_NO_UI;
385 
386     _SEH2_TRY
387     {
388 #ifdef ShellExecCmdLine
389         hr = ShellExecCmdLine(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
390                               SW_SHOWNORMAL, NULL, dwSeclFlags);
391 #else
392         hr = (*g_pShellExecCmdLine)(NULL, pEntry->pwszCommand, pEntry->pwszStartDir,
393                                     SW_SHOWNORMAL, NULL, dwSeclFlags);
394 #endif
395     }
396     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
397     {
398         hr = 0xDEADFACE;
399     }
400     _SEH2_END;
401 
402     ok(hr == pEntry->hr, "Line %d: hr expected 0x%lX, was 0x%lX\n", pEntry->lineno, pEntry->hr, hr);
403 
404 #define RETRY_COUNT     5
405 #define RETRY_INTERVAL  250
406     if (SUCCEEDED(hr) && pEntry->pwszWindowClass)
407     {
408         BOOL bFound = FALSE;
409         Sleep(RETRY_INTERVAL / 2);
410         for (int i = 0; i < RETRY_COUNT; ++i)
411         {
412             HWND hwnd = FindWindowW(pEntry->pwszWindowClass, NULL);
413             if (hwnd)
414             {
415                 bFound = TRUE;
416                 SendMessage(hwnd, WM_CLOSE, 0, 0);
417                 Sleep(RETRY_INTERVAL);
418                 break;
419             }
420             Sleep(RETRY_INTERVAL);
421         }
422         ok(bFound, "Line %d: The window not found\n", pEntry->lineno);
423     }
424 #undef RETRY_COUNT
425 #undef RETRY_INTERVAL
426 }
427 
428 START_TEST(ShellExecCmdLine)
429 {
430     using namespace std;
431 
432 #ifndef ShellExecCmdLine
433     HMODULE hShell32 = GetModuleHandleA("shell32");
434     g_pShellExecCmdLine = (SHELLEXECCMDLINE)GetProcAddress(hShell32, (LPCSTR)(INT_PTR)265);
435     if (!g_pShellExecCmdLine)
436     {
437         skip("ShellExecCmdLine is not found\n");
438         return;
439     }
440 #endif
441 
442     // s_testfile1
443     FILE *fp = fopen(s_testfile1, "wb");
444     ok(fp != NULL, "failed to create a test file\n");
445     fclose(fp);
446 
447     // s_testfile2
448     fp = fopen(s_testfile2, "wb");
449     ok(fp != NULL, "failed to create a test file\n");
450     if (fp)
451     {
452         fprintf(fp, "echo OK\n");
453     }
454     fclose(fp);
455 
456     for (size_t i = 0; i < _countof(s_entries); ++i)
457     {
458         DoEntry(&s_entries[i]);
459     }
460 
461     WCHAR buf0[MAX_PATH];
462     WCHAR buf1[MAX_PATH];
463     WCHAR buf2[MAX_PATH];
464     TEST_ENTRY additionals[] =
465     {
466         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, NULL },
467         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"." },
468         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf0, L"system32" },
469         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, NULL },
470         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"." },
471         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf1, L"system32" },
472         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, NULL },
473         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"." },
474         { __LINE__, CO_E_APPNOTFOUND, FALSE, L"Notepad", buf2, L"system32" },
475         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, NULL }, // FIXME
476         { __LINE__, S_OK, TRUE, L"Notepad", buf0, L"." },
477         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf0, L"system32" }, // FIXME
478         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf1, NULL }, // FIXME
479         { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"." },
480         { __LINE__, S_OK, TRUE, L"Notepad", buf1, L"system32" },
481         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, L"Notepad", buf2, NULL }, // FIXME
482         { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"." },
483         { __LINE__, S_OK, TRUE, L"Notepad", buf2, L"system32" },
484     };
485 
486     wsprintfW(buf0, L"%hs", s_testfile1);
487     wsprintfW(buf1, L"\"%hs\"", s_testfile1);
488     wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile1);
489     for (size_t i = 0; i < _countof(additionals); ++i)
490     {
491         DoEntry(&additionals[i]);
492     }
493 
494     TEST_ENTRY additionals2[] =
495     {
496         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, NULL },
497         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"." },
498         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf0, L"system32" },
499         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, NULL },
500         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"." },
501         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf1, L"system32" },
502         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, NULL },
503         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"." },
504         { __LINE__, CO_E_APPNOTFOUND, FALSE, NULL, buf2, L"system32" },
505         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, NULL }, // FIXME
506         { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
507         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf0, L"system32" },  // FIXME
508         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf1, NULL }, // FIXME
509         { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
510         { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
511         { __LINE__, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), TRUE, NULL, buf2, NULL }, // FIXME
512         { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
513         { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
514     };
515 
516     wsprintfW(buf0, L"%hs", s_testfile2);
517     wsprintfW(buf1, L"\"%hs\"", s_testfile2);
518     wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", s_testfile2);
519     for (size_t i = 0; i < _countof(additionals2); ++i)
520     {
521         DoEntry(&additionals2[i]);
522     }
523 
524     char path[MAX_PATH];
525     ok((INT_PTR)FindExecutableA("notepad.exe", NULL, s_notepad) >= 32, "FindExecutableA failed\n");
526     ok(GetModuleFileNameA(NULL, path, _countof(path)), "GetModuleFileNameA failed\n");
527     char *pch = strrchr(path, '\\');
528 
529     if (pch == NULL)
530     {
531         skip("pch == NULL\n");
532     }
533     else
534     {
535         // create "My Directory"
536         strcpy(pch, "\\My Directory");
537         if (GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES)
538             ok(CreateDirectoryA(path, NULL), "CreateDirectoryA failed\n");
539 
540         // create "My Directory\\Notepad.exe" as clone of Notepad.exe
541         strcpy(pch, "\\My Directory\\Notepad.exe");
542         ok(CopyFileA(s_notepad, path, FALSE), "CopyFileA failed\n");
543 
544         wsprintfW(buf0, L"%hs", path);
545         wsprintfW(buf1, L"\"%hs\"", path);
546         wsprintfW(buf2, L"\"%hs\" \"Test File.txt\"", path);
547         TEST_ENTRY additionals3[] =
548         {
549             { __LINE__, S_OK, FALSE, NULL, buf0, NULL },
550             { __LINE__, S_OK, FALSE, NULL, buf0, L"." },
551             { __LINE__, S_OK, FALSE, NULL, buf0, L"system32" },
552             { __LINE__, S_OK, FALSE, NULL, buf1, NULL },
553             { __LINE__, S_OK, FALSE, NULL, buf1, L"." },
554             { __LINE__, S_OK, FALSE, NULL, buf1, L"system32" },
555             { __LINE__, S_OK, FALSE, NULL, buf2, NULL },
556             { __LINE__, S_OK, FALSE, NULL, buf2, L"." },
557             { __LINE__, S_OK, FALSE, NULL, buf2, L"system32" },
558             { __LINE__, S_OK, TRUE, NULL, buf0, NULL },
559             { __LINE__, S_OK, TRUE, NULL, buf0, L"." },
560             { __LINE__, S_OK, TRUE, NULL, buf0, L"system32" },
561             { __LINE__, S_OK, TRUE, NULL, buf1, NULL },
562             { __LINE__, S_OK, TRUE, NULL, buf1, L"." },
563             { __LINE__, S_OK, TRUE, NULL, buf1, L"system32" },
564             { __LINE__, S_OK, TRUE, NULL, buf2, NULL },
565             { __LINE__, S_OK, TRUE, NULL, buf2, L"." },
566             { __LINE__, S_OK, TRUE, NULL, buf2, L"system32" },
567         };
568         for (size_t i = 0; i < _countof(additionals3); ++i)
569         {
570             DoEntry(&additionals3[i]);
571         }
572 
573         DeleteFileA(path);
574 
575         strcpy(pch, "\\My Directory");
576         RemoveDirectory(path);
577     }
578 
579     // clean up
580     ok(DeleteFileA(s_testfile1), "failed to delete the test file\n");
581     ok(DeleteFileA(s_testfile2), "failed to delete the test file\n");
582 }
583