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