Reactos
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
20static WCHAR s_win_dir[MAX_PATH];
21static WCHAR s_sys_dir[MAX_PATH];
22static WCHAR s_win_notepad[MAX_PATH];
23static WCHAR s_sys_notepad[MAX_PATH];
24static WCHAR s_win_test_exe[MAX_PATH];
25static WCHAR s_sys_test_exe[MAX_PATH];
26static WCHAR s_win_bat_file[MAX_PATH];
27static WCHAR s_sys_bat_file[MAX_PATH];
28static WCHAR s_win_txt_file[MAX_PATH];
29static WCHAR s_sys_txt_file[MAX_PATH];
30static WCHAR s_win_notepad_cmdline[MAX_PATH];
31static WCHAR s_sys_notepad_cmdline[MAX_PATH];
32static WCHAR s_win_test_exe_cmdline[MAX_PATH];
33static WCHAR s_sys_test_exe_cmdline[MAX_PATH];
34static BOOL s_bWow64;
35
36#define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
37
38typedef enum TEST_RESULT
39{
40 TEST_FAILED,
41 TEST_SUCCESS_NO_PROCESS,
42 TEST_SUCCESS_WITH_PROCESS,
43} TEST_RESULT;
44
45typedef struct TEST_ENTRY
46{
47 INT line;
48 TEST_RESULT result;
49 LPCWSTR lpFile;
50 LPCWSTR cmdline;
51} TEST_ENTRY, *PTEST_ENTRY;
52
53static void
54TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
55
56static 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
111static LPWSTR
112getCommandLineFromProcess(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
152static TEST_RESULT 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 (result == TEST_SUCCESS_WITH_PROCESS)
176 WaitForInputIdle(info.hProcess, 2000);
177
178 if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64)
179 {
180 LPWSTR cmdline = getCommandLineFromProcess(info.hProcess);
181 if (!cmdline)
182 {
183 skip("!cmdline\n");
184 }
185 else
186 {
187 ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0,
188 "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line,
189 pEntry->cmdline, cmdline);
190 }
191
192 TerminateProcess(info.hProcess, 0xDEADFACE);
193 free(cmdline);
194 }
195
196 CloseHandle(info.hProcess);
197 return result;
198}
199
200static void
201TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline)
202{
203 WINDOW_LIST existingwindows;
204 GetWindowList(&existingwindows);
205 HWND hWndForeground = GetForegroundWindow();
206
207 TEST_ENTRY entry = { line, result, lpFile, cmdline };
208 result = TEST_DoTestEntryStruct(&entry);
209
210 if (result == TEST_SUCCESS_NO_PROCESS)
211 {
212 // Wait a bit for Explorer to open its window
213 for (UINT i = 0; i < 2000 && hWndForeground == GetForegroundWindow(); i += 250)
214 Sleep(250);
215 }
216
217 CloseNewWindows(&existingwindows);
218 FreeWindowList(&existingwindows);
219}
220
221static BOOL
222enableTokenPrivilege(LPCWSTR pszPrivilege)
223{
224 HANDLE hToken;
225 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
226 return FALSE;
227
228 TOKEN_PRIVILEGES tkp = { 0 };
229 if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid))
230 return FALSE;
231
232 tkp.PrivilegeCount = 1;
233 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
234 return AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL);
235}
236
237static WINDOW_LIST s_List1, s_List2;
238
239static BOOL TEST_Start(void)
240{
241 // Check Wow64
242 s_bWow64 = FALSE;
243 IsWow64Process(GetCurrentProcess(), &s_bWow64);
244 if (s_bWow64)
245 skip("Wow64: Command Line check is skipped\n");
246
247 // getCommandLineFromProcess needs this
248 enableTokenPrivilege(SE_DEBUG_NAME);
249
250 // s_win_dir
251 GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir));
252
253 // s_sys_dir
254 GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir));
255
256 // s_win_notepad
257 GetWindowsDirectoryW(s_win_notepad, _countof(s_win_notepad));
258 PathAppendW(s_win_notepad, L"notepad.exe");
259
260 // s_sys_notepad
261 GetSystemDirectoryW(s_sys_notepad, _countof(s_sys_notepad));
262 PathAppendW(s_sys_notepad, L"notepad.exe");
263
264 // s_win_test_exe
265 GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
266 PathAppendW(s_win_test_exe, L"test program.exe");
267 BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE);
268 if (!ret)
269 {
270 skip("Please retry with admin rights\n");
271 return FALSE;
272 }
273
274 // s_sys_test_exe
275 GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe));
276 PathAppendW(s_sys_test_exe, L"test program.exe");
277 ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE);
278
279 // s_win_bat_file
280 GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file));
281 PathAppendW(s_win_bat_file, L"test program.bat");
282 FILE *fp = _wfopen(s_win_bat_file, L"wb");
283 fprintf(fp, "exit /b 3");
284 fclose(fp);
285 ok_int(PathFileExistsW(s_win_bat_file), TRUE);
286
287 // s_sys_bat_file
288 GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
289 PathAppendW(s_sys_bat_file, L"test program.bat");
290 fp = _wfopen(s_sys_bat_file, L"wb");
291 fprintf(fp, "exit /b 4");
292 fclose(fp);
293 ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
294
295 // s_win_txt_file
296 GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file));
297 PathAppendW(s_win_txt_file, L"test_file.txt");
298 fp = _wfopen(s_win_txt_file, L"wb");
299 fclose(fp);
300 ok_int(PathFileExistsW(s_win_txt_file), TRUE);
301
302 // s_sys_txt_file
303 GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file));
304 PathAppendW(s_sys_txt_file, L"test_file.txt");
305 fp = _wfopen(s_sys_txt_file, L"wb");
306 fclose(fp);
307 ok_int(PathFileExistsW(s_sys_txt_file), TRUE);
308
309 // Check .txt settings
310 WCHAR szPath[MAX_PATH];
311 FindExecutableW(s_sys_txt_file, NULL, szPath);
312 if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0)
313 {
314 skip("Please associate .txt with notepad.exe before tests\n");
315 return FALSE;
316 }
317
318 // command lines
319 StringCchPrintfW(s_win_notepad_cmdline, _countof(s_win_notepad_cmdline),
320 L"\"%s\" ", s_win_notepad);
321 StringCchPrintfW(s_sys_notepad_cmdline, _countof(s_sys_notepad_cmdline),
322 L"\"%s\" ", s_sys_notepad);
323 StringCchPrintfW(s_win_test_exe_cmdline, _countof(s_win_test_exe_cmdline),
324 L"\"%s\" ", s_win_test_exe);
325 StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline),
326 L"\"%s\" ", s_sys_test_exe);
327
328 GetWindowList(&s_List1);
329
330 return TRUE;
331}
332
333static void TEST_End(void)
334{
335 DeleteFileW(s_win_test_exe);
336 DeleteFileW(s_sys_test_exe);
337 DeleteFileW(s_win_txt_file);
338 DeleteFileW(s_sys_txt_file);
339 DeleteFileW(s_win_bat_file);
340 DeleteFileW(s_sys_bat_file);
341
342 // Execution can be asynchronous; you have to wait for it to finish.
343 INT nCount = GetWindowCount();
344 for (INT i = 0; i < 100; ++i)
345 {
346 INT nOldCount = nCount;
347 Sleep(3000);
348 nCount = GetWindowCount();
349 if (nOldCount == nCount)
350 break;
351 }
352 Sleep(3000);
353
354 // Close newly-opened window(s)
355 GetWindowList(&s_List2);
356 CloseNewWindows(&s_List1, &s_List2);
357 FreeWindowList(&s_List1);
358 FreeWindowList(&s_List2);
359}
360
361static void test_properties()
362{
363 HRESULT hrCoInit = CoInitialize(NULL);
364
365 WCHAR Buffer[MAX_PATH * 4];
366 GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
367
368 SHELLEXECUTEINFOW info = { sizeof(info) };
369 info.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI;
370 info.lpVerb = L"properties";
371 info.lpFile = Buffer;
372 info.nShow = SW_SHOW;
373
374 BOOL bRet = ShellExecuteExW(&info);
375 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
376 ok_ptr(info.hInstApp, (HINSTANCE)42);
377
378 WCHAR* Extension = PathFindExtensionW(Buffer);
379 if (Extension)
380 {
381 // The inclusion of this depends on the file display settings!
382 *Extension = UNICODE_NULL;
383 }
384
385 // Now retry it with the extension cut off
386 bRet = ShellExecuteExW(&info);
387 ok(bRet, "Failed! (GetLastError(): %d)\n", (int)GetLastError());
388 ok_ptr(info.hInstApp, (HINSTANCE)42);
389
390 // Now retry it with complete garabage
391 info.lpFile = L"complete garbage, cannot run this!";
392 bRet = ShellExecuteExW(&info);
393 ok_int(bRet, 0);
394 ok_ptr(info.hInstApp, (HINSTANCE)2);
395
396 if (SUCCEEDED(hrCoInit))
397 CoUninitialize();
398}
399
400static void test_sei_lpIDList()
401{
402 // Note: SEE_MASK_FLAG_NO_UI prevents the test from blocking with a MessageBox
403 WCHAR path[MAX_PATH];
404
405 /* This tests ShellExecuteEx with lpIDList for explorer C:\ */
406 GetSystemDirectoryW(path, _countof(path));
407 PathStripToRootW(path);
408 LPITEMIDLIST pidl = ILCreateFromPathW(path);
409 if (!pidl)
410 {
411 skip("Unable to initialize test\n");
412 return;
413 }
414
415 SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) };
416 ShellExecInfo.nShow = SW_SHOWNORMAL;
417 ShellExecInfo.fMask = SEE_MASK_IDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
418 ShellExecInfo.lpIDList = pidl;
419 BOOL ret = ShellExecuteExW(&ShellExecInfo);
420 ok_int(ret, TRUE);
421 ILFree(pidl);
422
423 /* This tests ShellExecuteEx with lpIDList going through IContextMenu */
424 CCoInit ComInit;
425 pidl = SHCloneSpecialIDList(NULL, CSIDL_PROFILE, TRUE);
426 if (!pidl)
427 {
428 skip("Unable to initialize test\n");
429 return;
430 }
431 ShellExecInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
432 ShellExecInfo.lpIDList = pidl;
433 ret = ShellExecuteExW(&ShellExecInfo);
434 ok_int(ret, TRUE);
435 ILFree(pidl);
436}
437
438static BOOL
439CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue)
440{
441 WCHAR szSubKey[MAX_PATH];
442 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
443
444 LSTATUS error;
445 HKEY hKey;
446 error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, 0, KEY_WRITE, NULL,
447 &hKey, NULL);
448 if (error != ERROR_SUCCESS)
449 trace("Could not create test key (%lu)\n", error);
450
451 DWORD cbValue = (lstrlenW(pszValue) + 1) * sizeof(WCHAR);
452 error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pszValue, cbValue);
453 if (error != ERROR_SUCCESS)
454 trace("Could not set value of the test key (%lu)\n", error);
455
456 RegCloseKey(hKey);
457
458 return error == ERROR_SUCCESS;
459}
460
461static VOID
462DeleteAppPath(LPCWSTR pszName)
463{
464 WCHAR szSubKey[MAX_PATH];
465 StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
466
467 LSTATUS error = RegDeleteKeyW(HKEY_LOCAL_MACHINE, szSubKey);
468 if (error != ERROR_SUCCESS)
469 trace("Could not remove the test key (%lu)\n", error);
470}
471
472static void TEST_AppPath(void)
473{
474 if (CreateAppPath(L"app_path_test.bat", s_win_test_exe))
475 {
476 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat");
477 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe");
478 DeleteAppPath(L"app_path_test.bat");
479 }
480
481 if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe))
482 {
483 TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat");
484 TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat.exe");
485 DeleteAppPath(L"app_path_test.bat.exe");
486 }
487}
488
489static void test_DoInvalidDir(void)
490{
491 WCHAR szSubProgram[MAX_PATH];
492 if (!FindSubProgram(szSubProgram, _countof(szSubProgram)))
493 {
494 skip("shell32_apitest_sub.exe not found\n");
495 return;
496 }
497
498 DWORD dwExitCode;
499 SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS };
500 sei.lpFile = szSubProgram;
501 sei.lpParameters = L"TEST";
502 sei.nShow = SW_SHOWNORMAL;
503
504 // Test invalid path on sei.lpDirectory
505 WCHAR szInvalidPath[MAX_PATH] = L"M:\\This is an invalid path\n";
506 sei.lpDirectory = szInvalidPath;
507 ok_int(ShellExecuteExW(&sei), TRUE);
508 WaitForSingleObject(sei.hProcess, 20 * 1000);
509 GetExitCodeProcess(sei.hProcess, &dwExitCode);
510 ok_long(dwExitCode, 0);
511 CloseHandle(sei.hProcess);
512}
513
514START_TEST(ShellExecuteEx)
515{
516#ifdef _WIN64
517 skip("Win64 is not supported yet\n");
518 return;
519#endif
520
521 if (!TEST_Start())
522 return;
523
524 TEST_AppPath();
525 TEST_DoTestEntries();
526 test_properties();
527 test_sei_lpIDList();
528 test_DoInvalidDir();
529
530 TEST_End();
531}