1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Testing ShellExecuteEx
5 * PROGRAMMER: Yaroslav Veremenko <yaroslav@veremenko.info>
6 * Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7 */
8
9 #include "shelltest.h"
10 #include "closewnd.h"
11 #include <pstypes.h>
12 #include <psfuncs.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <strsafe.h>
16 #include <versionhelpers.h>
17 #include <shellutils.h>
18 #include "shell32_apitest_sub.h"
19
20 static WCHAR s_win_dir[MAX_PATH];
21 static WCHAR s_sys_dir[MAX_PATH];
22 static WCHAR s_win_notepad[MAX_PATH];
23 static WCHAR s_sys_notepad[MAX_PATH];
24 static WCHAR s_win_test_exe[MAX_PATH];
25 static WCHAR s_sys_test_exe[MAX_PATH];
26 static WCHAR s_win_bat_file[MAX_PATH];
27 static WCHAR s_sys_bat_file[MAX_PATH];
28 static WCHAR s_win_txt_file[MAX_PATH];
29 static WCHAR s_sys_txt_file[MAX_PATH];
30 static WCHAR s_win_notepad_cmdline[MAX_PATH];
31 static WCHAR s_sys_notepad_cmdline[MAX_PATH];
32 static WCHAR s_win_test_exe_cmdline[MAX_PATH];
33 static WCHAR s_sys_test_exe_cmdline[MAX_PATH];
34 static BOOL s_bWow64;
35
36 #define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
37
38 typedef enum TEST_RESULT
39 {
40 TEST_FAILED,
41 TEST_SUCCESS_NO_PROCESS,
42 TEST_SUCCESS_WITH_PROCESS,
43 } TEST_RESULT;
44
45 typedef struct TEST_ENTRY
46 {
47 INT line;
48 TEST_RESULT result;
49 LPCWSTR lpFile;
50 LPCWSTR cmdline;
51 } TEST_ENTRY, *PTEST_ENTRY;
52
53 static void
54 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
55
TEST_DoTestEntries(void)56 static void TEST_DoTestEntries(void)
57 {
58 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, NULL);
59 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"");
60 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"This is an invalid path.");
61 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_bat_file, NULL);
62 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_test_exe, s_sys_test_exe_cmdline);
63 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_txt_file, NULL);
64 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_bat_file, NULL);
65 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_notepad, s_win_notepad_cmdline);
66 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_test_exe, s_win_test_exe_cmdline);
67 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_txt_file, NULL);
68 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad", s_sys_notepad_cmdline);
69 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad.exe", s_sys_notepad_cmdline);
70 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad.exe\"", s_sys_notepad_cmdline);
71 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"notepad\"", s_sys_notepad_cmdline);
72 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"test program.exe", s_sys_test_exe_cmdline);
73 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"test program.exe\"", s_sys_test_exe_cmdline);
74 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_win_dir);
75 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_sys_dir);
76 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"shell:ThisIsAnInvalidName");
77 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer
78 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer (with shell:)
79
80 if (!IsWindowsVistaOrGreater())
81 {
82 WCHAR szCurDir[MAX_PATH];
83 GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
84 SetCurrentDirectoryW(s_sys_dir);
85 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (without path)
86 SetCurrentDirectoryW(szCurDir);
87 }
88
89 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path)
90 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path and shell:)
91 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:AppData");
92 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Desktop");
93 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Programs");
94 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Start Menu");
95 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common StartUp");
96 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:ControlPanelFolder");
97 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Desktop");
98 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Favorites");
99 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Fonts");
100 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Local AppData");
101 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:My Pictures");
102 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Personal");
103 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Programs");
104 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Recent");
105 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:RecycleBinFolder");
106 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:SendTo");
107 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Start Menu");
108 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:StartUp");
109 }
110
111 static LPWSTR
getCommandLineFromProcess(HANDLE hProcess)112 getCommandLineFromProcess(HANDLE hProcess)
113 {
114 PEB peb;
115 PROCESS_BASIC_INFORMATION info;
116 RTL_USER_PROCESS_PARAMETERS Params;
117 NTSTATUS Status;
118 BOOL ret;
119
120 Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);
121 ok_ntstatus(Status, STATUS_SUCCESS);
122
123 ret = ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL);
124 if (!ret)
125 trace("ReadProcessMemory failed (%ld)\n", GetLastError());
126
127 ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL);
128 if (!ret)
129 trace("ReadProcessMemory failed (%ld)\n", GetLastError());
130
131 LPWSTR cmdline = Params.CommandLine.Buffer;
132 if (!cmdline)
133 trace("!cmdline\n");
134
135 SIZE_T cbCmdLine = Params.CommandLine.Length;
136 if (!cbCmdLine)
137 trace("!cbCmdLine\n");
138
139 LPWSTR pszBuffer = (LPWSTR)calloc(cbCmdLine + sizeof(WCHAR), 1);
140 if (!pszBuffer)
141 trace("!pszBuffer\n");
142
143 ret = ReadProcessMemory(hProcess, cmdline, pszBuffer, cbCmdLine, NULL);
144 if (!ret)
145 trace("ReadProcessMemory failed (%ld)\n", GetLastError());
146
147 pszBuffer[cbCmdLine / sizeof(WCHAR)] = UNICODE_NULL;
148
149 return pszBuffer; // needs free()
150 }
151
TEST_DoTestEntryStruct(const TEST_ENTRY * pEntry)152 static void TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry)
153 {
154 SHELLEXECUTEINFOW info = { sizeof(info) };
155 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE |
156 SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC;
157 info.hwnd = NULL;
158 info.lpVerb = NULL;
159 info.lpFile = pEntry->lpFile;
160 info.nShow = SW_SHOWNORMAL;
161
162 BOOL ret = ShellExecuteExW(&info);
163
164 TEST_RESULT result;
165 if (ret && info.hProcess)
166 result = TEST_SUCCESS_WITH_PROCESS;
167 else if (ret && !info.hProcess)
168 result = TEST_SUCCESS_NO_PROCESS;
169 else
170 result = TEST_FAILED;
171
172 ok(pEntry->result == result,
173 "Line %d: result: %d vs %d\n", pEntry->line, pEntry->result, result);
174
175 if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64)
176 {
177 LPWSTR cmdline = getCommandLineFromProcess(info.hProcess);
178 if (!cmdline)
179 {
180 skip("!cmdline\n");
181 }
182 else
183 {
184 ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0,
185 "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line,
186 pEntry->cmdline, cmdline);
187 }
188
189 TerminateProcess(info.hProcess, 0xDEADFACE);
190 free(cmdline);
191 }
192
193 CloseHandle(info.hProcess);
194 }
195
196 static void
TEST_DoTestEntry(INT line,TEST_RESULT result,LPCWSTR lpFile,LPCWSTR cmdline)197 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline)
198 {
199 TEST_ENTRY entry = { line, result, lpFile, cmdline };
200 TEST_DoTestEntryStruct(&entry);
201 }
202
203 static BOOL
enableTokenPrivilege(LPCWSTR pszPrivilege)204 enableTokenPrivilege(LPCWSTR pszPrivilege)
205 {
206 HANDLE hToken;
207 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
208 return FALSE;
209
210 TOKEN_PRIVILEGES tkp = { 0 };
211 if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid))
212 return FALSE;
213
214 tkp.PrivilegeCount = 1;
215 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
216 return AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL);
217 }
218
219 static WINDOW_LIST s_List1, s_List2;
220
TEST_Start(void)221 static BOOL TEST_Start(void)
222 {
223 // Check Wow64
224 s_bWow64 = FALSE;
225 IsWow64Process(GetCurrentProcess(), &s_bWow64);
226 if (s_bWow64)
227 skip("Wow64: Command Line check is skipped\n");
228
229 // getCommandLineFromProcess needs this
230 enableTokenPrivilege(SE_DEBUG_NAME);
231
232 // s_win_dir
233 GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir));
234
235 // s_sys_dir
236 GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir));
237
238 // s_win_notepad
239 GetWindowsDirectoryW(s_win_notepad, _countof(s_win_notepad));
240 PathAppendW(s_win_notepad, L"notepad.exe");
241
242 // s_sys_notepad
243 GetSystemDirectoryW(s_sys_notepad, _countof(s_sys_notepad));
244 PathAppendW(s_sys_notepad, L"notepad.exe");
245
246 // s_win_test_exe
247 GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
248 PathAppendW(s_win_test_exe, L"test program.exe");
249 BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE);
250 if (!ret)
251 {
252 skip("Please retry with admin rights\n");
253 return FALSE;
254 }
255
256 // s_sys_test_exe
257 GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe));
258 PathAppendW(s_sys_test_exe, L"test program.exe");
259 ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE);
260
261 // s_win_bat_file
262 GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file));
263 PathAppendW(s_win_bat_file, L"test program.bat");
264 FILE *fp = _wfopen(s_win_bat_file, L"wb");
265 fprintf(fp, "exit /b 3");
266 fclose(fp);
267 ok_int(PathFileExistsW(s_win_bat_file), TRUE);
268
269 // s_sys_bat_file
270 GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
271 PathAppendW(s_sys_bat_file, L"test program.bat");
272 fp = _wfopen(s_sys_bat_file, L"wb");
273 fprintf(fp, "exit /b 4");
274 fclose(fp);
275 ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
276
277 // s_win_txt_file
278 GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file));
279 PathAppendW(s_win_txt_file, L"test_file.txt");
280 fp = _wfopen(s_win_txt_file, L"wb");
281 fclose(fp);
282 ok_int(PathFileExistsW(s_win_txt_file), TRUE);
283
284 // s_sys_txt_file
285 GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file));
286 PathAppendW(s_sys_txt_file, L"test_file.txt");
287 fp = _wfopen(s_sys_txt_file, L"wb");
288 fclose(fp);
289 ok_int(PathFileExistsW(s_sys_txt_file), TRUE);
290
291 // Check .txt settings
292 WCHAR szPath[MAX_PATH];
293 FindExecutableW(s_sys_txt_file, NULL, szPath);
294 if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0)
295 {
296 skip("Please associate .txt with notepad.exe before tests\n");
297 return FALSE;
298 }
299
300 // command lines
301 StringCchPrintfW(s_win_notepad_cmdline, _countof(s_win_notepad_cmdline),
302 L"\"%s\" ", s_win_notepad);
303 StringCchPrintfW(s_sys_notepad_cmdline, _countof(s_sys_notepad_cmdline),
304 L"\"%s\" ", s_sys_notepad);
305 StringCchPrintfW(s_win_test_exe_cmdline, _countof(s_win_test_exe_cmdline),
306 L"\"%s\" ", s_win_test_exe);
307 StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline),
308 L"\"%s\" ", s_sys_test_exe);
309
310 GetWindowList(&s_List1);
311
312 return TRUE;
313 }
314
TEST_End(void)315 static void TEST_End(void)
316 {
317 GetWindowListForClose(&s_List2);
318 CloseNewWindows(&s_List1, &s_List2);
319 FreeWindowList(&s_List1);
320 FreeWindowList(&s_List2);
321
322 DeleteFileW(s_win_test_exe);
323 DeleteFileW(s_sys_test_exe);
324 DeleteFileW(s_win_txt_file);
325 DeleteFileW(s_sys_txt_file);
326 DeleteFileW(s_win_bat_file);
327 DeleteFileW(s_sys_bat_file);
328 }
329
test_properties()330 static void test_properties()
331 {
332 HRESULT hrCoInit = CoInitialize(NULL);
333
334 WCHAR Buffer[MAX_PATH * 4];
335 GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
336
337 SHELLEXECUTEINFOW info = { sizeof(info) };
338 info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI;
339 info.lpVerb = L"properties";
340 info.lpFile = Buffer;
341 info.nShow = SW_SHOW;
342
343 BOOL bRet = ShellExecuteExW(&info);
344 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
345 ok_ptr(info.hInstApp, (HINSTANCE)42);
346
347 WCHAR* Extension = PathFindExtensionW(Buffer);
348 if (Extension)
349 {
350 // The inclusion of this depends on the file display settings!
351 *Extension = UNICODE_NULL;
352 }
353
354 // Now retry it with the extension cut off
355 bRet = ShellExecuteExW(&info);
356 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
357 ok_ptr(info.hInstApp, (HINSTANCE)42);
358
359 // Now retry it with complete garabage
360 info.lpFile = L"complete garbage, cannot run this!";
361 bRet = ShellExecuteExW(&info);
362 ok_int(bRet, 0);
363 ok_ptr(info.hInstApp, (HINSTANCE)2);
364
365 if (SUCCEEDED(hrCoInit))
366 CoUninitialize();
367 }
368
test_sei_lpIDList()369 static void test_sei_lpIDList()
370 {
371 // Note: SEE_MASK_FLAG_NO_UI prevents the test from blocking with a MessageBox
372 WCHAR path[MAX_PATH];
373
374 /* This tests ShellExecuteEx with lpIDList for explorer C:\ */
375 GetSystemDirectoryW(path, _countof(path));
376 PathStripToRootW(path);
377 LPITEMIDLIST pidl = ILCreateFromPathW(path);
378 if (!pidl)
379 {
380 skip("Unable to initialize test\n");
381 return;
382 }
383
384 SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) };
385 ShellExecInfo.nShow = SW_SHOWNORMAL;
386 ShellExecInfo.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
387 ShellExecInfo.lpIDList = pidl;
388 BOOL ret = ShellExecuteExW(&ShellExecInfo);
389 ok_int(ret, TRUE);
390 ILFree(pidl);
391
392 /* This tests ShellExecuteEx with lpIDList going through IContextMenu */
393 CCoInit ComInit;
394 pidl = SHCloneSpecialIDList(NULL, CSIDL_PROFILE, TRUE);
395 if (!pidl)
396 {
397 skip("Unable to initialize test\n");
398 return;
399 }
400 ShellExecInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
401 ShellExecInfo.lpIDList = pidl;
402 ret = ShellExecuteExW(&ShellExecInfo);
403 ok_int(ret, TRUE);
404 ILFree(pidl);
405 }
406
407 static BOOL
CreateAppPath(LPCWSTR pszName,LPCWSTR pszValue)408 CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue)
409 {
410 WCHAR szSubKey[MAX_PATH];
411 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
412
413 LSTATUS error;
414 HKEY hKey;
415 error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, 0, KEY_WRITE, NULL,
416 &hKey, NULL);
417 if (error != ERROR_SUCCESS)
418 trace("Could not create test key (%lu)\n", error);
419
420 DWORD cbValue = (lstrlenW(pszValue) + 1) * sizeof(WCHAR);
421 error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pszValue, cbValue);
422 if (error != ERROR_SUCCESS)
423 trace("Could not set value of the test key (%lu)\n", error);
424
425 RegCloseKey(hKey);
426
427 return error == ERROR_SUCCESS;
428 }
429
430 static VOID
DeleteAppPath(LPCWSTR pszName)431 DeleteAppPath(LPCWSTR pszName)
432 {
433 WCHAR szSubKey[MAX_PATH];
434 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
435
436 LSTATUS error = RegDeleteKeyW(HKEY_LOCAL_MACHINE, szSubKey);
437 if (error != ERROR_SUCCESS)
438 trace("Could not remove the test key (%lu)\n", error);
439 }
440
TEST_AppPath(void)441 static void TEST_AppPath(void)
442 {
443 if (CreateAppPath(L"app_path_test.bat", s_win_test_exe))
444 {
445 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat");
446 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe");
447 DeleteAppPath(L"app_path_test.bat");
448 }
449
450 if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe))
451 {
452 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat");
453 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat.exe");
454 DeleteAppPath(L"app_path_test.bat.exe");
455 }
456 }
457
test_DoInvalidDir(void)458 static void test_DoInvalidDir(void)
459 {
460 WCHAR szSubProgram[MAX_PATH];
461 if (!FindSubProgram(szSubProgram, _countof(szSubProgram)))
462 {
463 skip("shell32_apitest_sub.exe not found\n");
464 return;
465 }
466
467 DWORD dwExitCode;
468 SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS };
469 sei.lpFile = szSubProgram;
470 sei.lpParameters = L"TEST";
471 sei.nShow = SW_SHOWNORMAL;
472
473 // Test invalid path on sei.lpDirectory
474 WCHAR szInvalidPath[MAX_PATH] = L"M:\\This is an invalid path\n";
475 sei.lpDirectory = szInvalidPath;
476 ok_int(ShellExecuteExW(&sei), TRUE);
477 WaitForSingleObject(sei.hProcess, 20 * 1000);
478 GetExitCodeProcess(sei.hProcess, &dwExitCode);
479 ok_long(dwExitCode, 0);
480 CloseHandle(sei.hProcess);
481 }
482
START_TEST(ShellExecuteEx)483 START_TEST(ShellExecuteEx)
484 {
485 #ifdef _WIN64
486 skip("Win64 is not supported yet\n");
487 return;
488 #endif
489
490 if (!TEST_Start())
491 return;
492
493 TEST_AppPath();
494 TEST_DoTestEntries();
495 test_properties();
496 test_sei_lpIDList();
497 test_DoInvalidDir();
498
499 TEST_End();
500 }
501