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 "shell32_apitest_sub.h"
18 
19 static WCHAR s_win_dir[MAX_PATH];
20 static WCHAR s_sys_dir[MAX_PATH];
21 static WCHAR s_win_notepad[MAX_PATH];
22 static WCHAR s_sys_notepad[MAX_PATH];
23 static WCHAR s_win_test_exe[MAX_PATH];
24 static WCHAR s_sys_test_exe[MAX_PATH];
25 static WCHAR s_win_bat_file[MAX_PATH];
26 static WCHAR s_sys_bat_file[MAX_PATH];
27 static WCHAR s_win_txt_file[MAX_PATH];
28 static WCHAR s_sys_txt_file[MAX_PATH];
29 static WCHAR s_win_notepad_cmdline[MAX_PATH];
30 static WCHAR s_sys_notepad_cmdline[MAX_PATH];
31 static WCHAR s_win_test_exe_cmdline[MAX_PATH];
32 static WCHAR s_sys_test_exe_cmdline[MAX_PATH];
33 static BOOL s_bWow64;
34 
35 #define REG_APPPATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
36 
37 typedef enum TEST_RESULT
38 {
39     TEST_FAILED,
40     TEST_SUCCESS_NO_PROCESS,
41     TEST_SUCCESS_WITH_PROCESS,
42 } TEST_RESULT;
43 
44 typedef struct TEST_ENTRY
45 {
46     INT line;
47     TEST_RESULT result;
48     LPCWSTR lpFile;
49     LPCWSTR cmdline;
50 } TEST_ENTRY, *PTEST_ENTRY;
51 
52 static void
53 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline = NULL);
54 
55 static void TEST_DoTestEntries(void)
56 {
57     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, NULL);
58     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"");
59     TEST_DoTestEntry(__LINE__, TEST_FAILED, L"This is an invalid path.");
60     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_bat_file, NULL);
61     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_test_exe, s_sys_test_exe_cmdline);
62     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_sys_txt_file, NULL);
63     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_bat_file, NULL);
64     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_notepad, s_win_notepad_cmdline);
65     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_test_exe, s_win_test_exe_cmdline);
66     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, s_win_txt_file, NULL);
67     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad", s_sys_notepad_cmdline);
68     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"notepad.exe", 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\"", s_sys_notepad_cmdline);
71     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"test program.exe", s_sys_test_exe_cmdline);
72     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"\"test program.exe\"", s_sys_test_exe_cmdline);
73     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_win_dir);
74     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, s_sys_dir);
75     TEST_DoTestEntry(__LINE__, TEST_FAILED, L"shell:ThisIsAnInvalidName");
76     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer
77     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // My Computer (with shell:)
78 
79     if (!IsWindowsVistaOrGreater())
80     {
81         WCHAR szCurDir[MAX_PATH];
82         GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
83         SetCurrentDirectoryW(s_sys_dir);
84         TEST_DoTestEntry(__LINE__, TEST_FAILED, L"::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (without path)
85         SetCurrentDirectoryW(szCurDir);
86     }
87 
88     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"); // Control Panel (with path)
89     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:)
90     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:AppData");
91     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Desktop");
92     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Programs");
93     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common Start Menu");
94     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Common StartUp");
95     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:ControlPanelFolder");
96     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Desktop");
97     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Favorites");
98     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Fonts");
99     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Local AppData");
100     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:My Pictures");
101     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Personal");
102     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Programs");
103     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Recent");
104     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:RecycleBinFolder");
105     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:SendTo");
106     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:Start Menu");
107     TEST_DoTestEntry(__LINE__, TEST_SUCCESS_NO_PROCESS, L"shell:StartUp");
108 }
109 
110 static LPWSTR
111 getCommandLineFromProcess(HANDLE hProcess)
112 {
113     PEB peb;
114     PROCESS_BASIC_INFORMATION info;
115     RTL_USER_PROCESS_PARAMETERS Params;
116     NTSTATUS Status;
117     BOOL ret;
118 
119     Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &info, sizeof(info), NULL);
120     ok_ntstatus(Status, STATUS_SUCCESS);
121 
122     ret = ReadProcessMemory(hProcess, info.PebBaseAddress, &peb, sizeof(peb), NULL);
123     if (!ret)
124         trace("ReadProcessMemory failed (%ld)\n", GetLastError());
125 
126     ReadProcessMemory(hProcess, peb.ProcessParameters, &Params, sizeof(Params), NULL);
127     if (!ret)
128         trace("ReadProcessMemory failed (%ld)\n", GetLastError());
129 
130     LPWSTR cmdline = Params.CommandLine.Buffer;
131     if (!cmdline)
132         trace("!cmdline\n");
133 
134     SIZE_T cbCmdLine = Params.CommandLine.Length;
135     if (!cbCmdLine)
136         trace("!cbCmdLine\n");
137 
138     LPWSTR pszBuffer = (LPWSTR)calloc(cbCmdLine + sizeof(WCHAR), 1);
139     if (!pszBuffer)
140         trace("!pszBuffer\n");
141 
142     ret = ReadProcessMemory(hProcess, cmdline, pszBuffer, cbCmdLine, NULL);
143     if (!ret)
144         trace("ReadProcessMemory failed (%ld)\n", GetLastError());
145 
146     pszBuffer[cbCmdLine / sizeof(WCHAR)] = UNICODE_NULL;
147 
148     return pszBuffer; // needs free()
149 }
150 
151 static void TEST_DoTestEntryStruct(const TEST_ENTRY *pEntry)
152 {
153     SHELLEXECUTEINFOW info = { sizeof(info) };
154     info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_WAITFORINPUTIDLE |
155                  SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC;
156     info.hwnd = NULL;
157     info.lpVerb = NULL;
158     info.lpFile = pEntry->lpFile;
159     info.nShow = SW_SHOWNORMAL;
160 
161     BOOL ret = ShellExecuteExW(&info);
162 
163     TEST_RESULT result;
164     if (ret && info.hProcess)
165         result = TEST_SUCCESS_WITH_PROCESS;
166     else if (ret && !info.hProcess)
167         result = TEST_SUCCESS_NO_PROCESS;
168     else
169         result = TEST_FAILED;
170 
171     ok(pEntry->result == result,
172        "Line %d: result: %d vs %d\n", pEntry->line, pEntry->result, result);
173 
174     if (pEntry->result == TEST_SUCCESS_WITH_PROCESS && pEntry->cmdline && !s_bWow64)
175     {
176         LPWSTR cmdline = getCommandLineFromProcess(info.hProcess);
177         if (!cmdline)
178         {
179             skip("!cmdline\n");
180         }
181         else
182         {
183             ok(lstrcmpiW(pEntry->cmdline, cmdline) == 0,
184                "Line %d: cmdline: '%ls' vs '%ls'\n", pEntry->line,
185                pEntry->cmdline, cmdline);
186         }
187 
188         TerminateProcess(info.hProcess, 0xDEADFACE);
189         free(cmdline);
190     }
191 
192     CloseHandle(info.hProcess);
193 }
194 
195 static void
196 TEST_DoTestEntry(INT line, TEST_RESULT result, LPCWSTR lpFile, LPCWSTR cmdline)
197 {
198     TEST_ENTRY entry = { line, result, lpFile, cmdline };
199     TEST_DoTestEntryStruct(&entry);
200 }
201 
202 static BOOL
203 enableTokenPrivilege(LPCWSTR pszPrivilege)
204 {
205     HANDLE hToken;
206     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
207         return FALSE;
208 
209     TOKEN_PRIVILEGES tkp = { 0 };
210     if (!LookupPrivilegeValueW(NULL, pszPrivilege, &tkp.Privileges[0].Luid))
211         return FALSE;
212 
213     tkp.PrivilegeCount = 1;
214     tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
215     return AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL);
216 }
217 
218 static WINDOW_LIST s_List1, s_List2;
219 
220 static BOOL TEST_Start(void)
221 {
222     // Check Wow64
223     s_bWow64 = FALSE;
224     IsWow64Process(GetCurrentProcess(), &s_bWow64);
225     if (s_bWow64)
226         skip("Wow64: Command Line check is skipped\n");
227 
228     // getCommandLineFromProcess needs this
229     enableTokenPrivilege(SE_DEBUG_NAME);
230 
231     // s_win_dir
232     GetWindowsDirectoryW(s_win_dir, _countof(s_win_dir));
233 
234     // s_sys_dir
235     GetSystemDirectoryW(s_sys_dir, _countof(s_sys_dir));
236 
237     // s_win_notepad
238     GetWindowsDirectoryW(s_win_notepad, _countof(s_win_notepad));
239     PathAppendW(s_win_notepad, L"notepad.exe");
240 
241     // s_sys_notepad
242     GetSystemDirectoryW(s_sys_notepad, _countof(s_sys_notepad));
243     PathAppendW(s_sys_notepad, L"notepad.exe");
244 
245     // s_win_test_exe
246     GetWindowsDirectoryW(s_win_test_exe, _countof(s_win_test_exe));
247     PathAppendW(s_win_test_exe, L"test program.exe");
248     BOOL ret = CopyFileW(s_win_notepad, s_win_test_exe, FALSE);
249     if (!ret)
250     {
251         skip("Please retry with admin rights\n");
252         return FALSE;
253     }
254 
255     // s_sys_test_exe
256     GetSystemDirectoryW(s_sys_test_exe, _countof(s_sys_test_exe));
257     PathAppendW(s_sys_test_exe, L"test program.exe");
258     ok_int(CopyFileW(s_win_notepad, s_sys_test_exe, FALSE), TRUE);
259 
260     // s_win_bat_file
261     GetWindowsDirectoryW(s_win_bat_file, _countof(s_win_bat_file));
262     PathAppendW(s_win_bat_file, L"test program.bat");
263     FILE *fp = _wfopen(s_win_bat_file, L"wb");
264     fprintf(fp, "exit /b 3");
265     fclose(fp);
266     ok_int(PathFileExistsW(s_win_bat_file), TRUE);
267 
268     // s_sys_bat_file
269     GetSystemDirectoryW(s_sys_bat_file, _countof(s_sys_bat_file));
270     PathAppendW(s_sys_bat_file, L"test program.bat");
271     fp = _wfopen(s_sys_bat_file, L"wb");
272     fprintf(fp, "exit /b 4");
273     fclose(fp);
274     ok_int(PathFileExistsW(s_sys_bat_file), TRUE);
275 
276     // s_win_txt_file
277     GetWindowsDirectoryW(s_win_txt_file, _countof(s_win_txt_file));
278     PathAppendW(s_win_txt_file, L"test_file.txt");
279     fp = _wfopen(s_win_txt_file, L"wb");
280     fclose(fp);
281     ok_int(PathFileExistsW(s_win_txt_file), TRUE);
282 
283     // s_sys_txt_file
284     GetSystemDirectoryW(s_sys_txt_file, _countof(s_sys_txt_file));
285     PathAppendW(s_sys_txt_file, L"test_file.txt");
286     fp = _wfopen(s_sys_txt_file, L"wb");
287     fclose(fp);
288     ok_int(PathFileExistsW(s_sys_txt_file), TRUE);
289 
290     // Check .txt settings
291     WCHAR szPath[MAX_PATH];
292     FindExecutableW(s_sys_txt_file, NULL, szPath);
293     if (lstrcmpiW(PathFindFileNameW(szPath), L"notepad.exe") != 0)
294     {
295         skip("Please associate .txt with notepad.exe before tests\n");
296         return FALSE;
297     }
298 
299     // command lines
300     StringCchPrintfW(s_win_notepad_cmdline, _countof(s_win_notepad_cmdline),
301                      L"\"%s\" ", s_win_notepad);
302     StringCchPrintfW(s_sys_notepad_cmdline, _countof(s_sys_notepad_cmdline),
303                      L"\"%s\" ", s_sys_notepad);
304     StringCchPrintfW(s_win_test_exe_cmdline, _countof(s_win_test_exe_cmdline),
305                      L"\"%s\" ", s_win_test_exe);
306     StringCchPrintfW(s_sys_test_exe_cmdline, _countof(s_sys_test_exe_cmdline),
307                      L"\"%s\" ", s_sys_test_exe);
308 
309     GetWindowList(&s_List1);
310 
311     return TRUE;
312 }
313 
314 static void TEST_End(void)
315 {
316     Sleep(500);
317     GetWindowList(&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 
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 
369 static void test_sei_lpIDList()
370 {
371     if (IsWindowsVistaOrGreater())
372     {
373         skip("Vista+\n");
374         return;
375     }
376 
377     /* This tests ShellExecuteEx with lpIDList for explorer C:\ */
378 
379     /* ITEMIDLIST for CLSID of 'My Computer' followed by PIDL for 'C:\' */
380     BYTE lpitemidlist[30] = { 0x14, 0, 0x1f, 0, 0xe0, 0x4f, 0xd0, 0x20, 0xea,
381     0x3a, 0x69, 0x10, 0xa2, 0xd8, 0x08, 0, 0x2b, 0x30, 0x30, 0x9d, // My Computer
382     0x8, 0, 0x23, 0x43, 0x3a, 0x5c, 0x5c, 0, 0, 0,}; // C:\\ + NUL-NUL ending
383 
384     SHELLEXECUTEINFOW ShellExecInfo = { sizeof(ShellExecInfo) };
385     ShellExecInfo.fMask = SEE_MASK_IDLIST;
386     ShellExecInfo.hwnd = NULL;
387     ShellExecInfo.nShow = SW_SHOWNORMAL;
388     ShellExecInfo.lpIDList = lpitemidlist;
389     BOOL ret = ShellExecuteExW(&ShellExecInfo);
390     ok_int(ret, TRUE);
391 }
392 
393 static BOOL
394 CreateAppPath(LPCWSTR pszName, LPCWSTR pszValue)
395 {
396     WCHAR szSubKey[MAX_PATH];
397     StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
398 
399     LSTATUS error;
400     HKEY hKey;
401     error = RegCreateKeyExW(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, 0, KEY_WRITE, NULL,
402                             &hKey, NULL);
403     if (error != ERROR_SUCCESS)
404         trace("Could not create test key (%lu)\n", error);
405 
406     DWORD cbValue = (lstrlenW(pszValue) + 1) * sizeof(WCHAR);
407     error = RegSetValueExW(hKey, NULL, 0, REG_SZ, (LPBYTE)pszValue, cbValue);
408     if (error != ERROR_SUCCESS)
409         trace("Could not set value of the test key (%lu)\n", error);
410 
411     RegCloseKey(hKey);
412 
413     return error == ERROR_SUCCESS;
414 }
415 
416 static VOID
417 DeleteAppPath(LPCWSTR pszName)
418 {
419     WCHAR szSubKey[MAX_PATH];
420     StringCchPrintfW(szSubKey, _countof(szSubKey), L"%s\\%s", REG_APPPATHS, pszName);
421 
422     LSTATUS error = RegDeleteKeyW(HKEY_LOCAL_MACHINE, szSubKey);
423     if (error != ERROR_SUCCESS)
424         trace("Could not remove the test key (%lu)\n", error);
425 }
426 
427 static void TEST_AppPath(void)
428 {
429     if (CreateAppPath(L"app_path_test.bat", s_win_test_exe))
430     {
431         TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat");
432         TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat.exe");
433         DeleteAppPath(L"app_path_test.bat");
434     }
435 
436     if (CreateAppPath(L"app_path_test.bat.exe", s_sys_test_exe))
437     {
438         TEST_DoTestEntry(__LINE__, TEST_FAILED, L"app_path_test.bat");
439         TEST_DoTestEntry(__LINE__, TEST_SUCCESS_WITH_PROCESS, L"app_path_test.bat.exe");
440         DeleteAppPath(L"app_path_test.bat.exe");
441     }
442 }
443 
444 static void test_DoInvalidDir(void)
445 {
446     WCHAR szSubProgram[MAX_PATH];
447     if (!FindSubProgram(szSubProgram, _countof(szSubProgram)))
448     {
449         skip("shell32_apitest_sub.exe not found\n");
450         return;
451     }
452 
453     DWORD dwExitCode;
454     SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS };
455     sei.lpFile = szSubProgram;
456     sei.lpParameters = L"TEST";
457     sei.nShow = SW_SHOWNORMAL;
458 
459     // Test invalid path on sei.lpDirectory
460     WCHAR szInvalidPath[MAX_PATH] = L"M:\\This is an invalid path\n";
461     sei.lpDirectory = szInvalidPath;
462     ok_int(ShellExecuteExW(&sei), TRUE);
463     WaitForSingleObject(sei.hProcess, 20 * 1000);
464     GetExitCodeProcess(sei.hProcess, &dwExitCode);
465     ok_long(dwExitCode, 0);
466     CloseHandle(sei.hProcess);
467 }
468 
469 START_TEST(ShellExecuteEx)
470 {
471 #ifdef _WIN64
472     skip("Win64 is not supported yet\n");
473     return;
474 #endif
475 
476     if (!TEST_Start())
477         return;
478 
479     TEST_AppPath();
480     TEST_DoTestEntries();
481     test_properties();
482     test_sei_lpIDList();
483     test_DoInvalidDir();
484 
485     TEST_End();
486 }
487