2 * Unit test of the ShellExecute function.
4 * Copyright 2005 Francois Gouget for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * - test the default verb selection
23 * - test selection of an alternate class
24 * - try running executables in more ways
25 * - try passing arguments to executables
26 * - ShellExecute("foo.shlexec") with no path should work if foo.shlexec is
28 * - test associations that use %l, %L or "%1" instead of %1
29 * - we may want to test ShellExecuteEx() instead of ShellExecute()
30 * and then we could also check its return value
31 * - ShellExecuteEx() also calls SetLastError() with meaningful values which
43 #include "wine/test.h"
45 #include "shell32_test.h"
48 static char argv0[MAX_PATH];
51 static char tmpdir[MAX_PATH];
53 static const char* testfiles[]=
55 "%s\\test file.shlexec",
56 "%s\\test file.noassoc",
57 "%s\\test file.noassoc.shlexec",
58 "%s\\test file.shlexec.noassoc",
59 "%s\\test_shortcut_shlexec.lnk",
64 static void strcat_param(char* str, const char* param)
78 static char shell_call[2048]="";
79 static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
81 strcpy(shell_call, "ShellExecute(");
82 strcat_param(shell_call, operation);
83 strcat(shell_call, ", ");
84 strcat_param(shell_call, file);
85 strcat(shell_call, ", ");
86 strcat_param(shell_call, parameters);
87 strcat(shell_call, ", ");
88 strcat_param(shell_call, directory);
89 strcat(shell_call, ")");
90 if (winetest_debug > 1)
91 trace("%s\n", shell_call);
93 SetLastError(0xcafebabe);
94 /* FIXME: We cannot use ShellExecuteEx() here because if there is no
95 * association it displays the 'Open With' dialog and I could not find
96 * a flag to prevent this.
98 return (int)ShellExecute(NULL, operation, file, parameters, directory,
102 static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
103 LPCSTR parameters, LPCSTR directory)
105 SHELLEXECUTEINFO sei;
109 strcpy(shell_call, "ShellExecuteEx(");
110 strcat_param(shell_call, operation);
111 strcat(shell_call, ", ");
112 strcat_param(shell_call, file);
113 strcat(shell_call, ", ");
114 strcat_param(shell_call, parameters);
115 strcat(shell_call, ", ");
116 strcat_param(shell_call, directory);
117 strcat(shell_call, ")");
118 if (winetest_debug > 1)
119 trace("%s\n", shell_call);
121 sei.cbSize=sizeof(sei);
124 sei.lpVerb=operation;
126 sei.lpParameters=parameters;
127 sei.lpDirectory=directory;
128 sei.nShow=SW_SHOWNORMAL;
129 sei.hInstApp=NULL; /* Out */
136 SetLastError(0xcafebabe);
137 success=ShellExecuteEx(&sei);
138 rc=(int)sei.hInstApp;
139 ok((success && rc >= 32) || (!success && rc < 32),
140 "%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
144 static void create_test_association(const char* extension)
146 HKEY hkey, hkey_shell;
147 char class[MAX_PATH];
150 sprintf(class, "shlexec%s", extension);
151 rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
153 assert(rc==ERROR_SUCCESS);
154 rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, class, strlen(class)+1);
155 assert(rc==ERROR_SUCCESS);
158 rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
159 KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
160 assert(rc==ERROR_SUCCESS);
161 rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
162 KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
163 assert(rc==ERROR_SUCCESS);
165 CloseHandle(hkey_shell);
168 static void delete_test_association(const char* extension)
170 char class[MAX_PATH];
172 sprintf(class, "shlexec%s", extension);
173 SHDeleteKey(HKEY_CLASSES_ROOT, class);
174 SHDeleteKey(HKEY_CLASSES_ROOT, extension);
177 static void create_test_verb(const char* extension, const char* verb)
179 HKEY hkey_shell, hkey_verb, hkey_cmd;
180 char shell[MAX_PATH];
184 sprintf(shell, "shlexec%s\\shell", extension);
185 rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
186 KEY_CREATE_SUB_KEY, &hkey_shell);
187 assert(rc==ERROR_SUCCESS);
188 rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
189 NULL, &hkey_verb, NULL);
190 assert(rc==ERROR_SUCCESS);
191 rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
192 NULL, &hkey_cmd, NULL);
193 assert(rc==ERROR_SUCCESS);
195 cmd=malloc(strlen(argv0)+13+1);
196 sprintf(cmd,"%s shlexec \"%%1\"", argv0);
197 rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, cmd, strlen(cmd)+1);
198 assert(rc==ERROR_SUCCESS);
201 CloseHandle(hkey_shell);
202 CloseHandle(hkey_verb);
203 CloseHandle(hkey_cmd);
214 static filename_tests_t filename_tests[]=
216 /* Test bad / nonexistent filenames */
217 {"%s\\nonexistent.shlexec", ERROR_FILE_NOT_FOUND, 1},
218 {"%s\\nonexistent.noassoc", ERROR_FILE_NOT_FOUND, 1},
221 {"%s\\test file.shlexec", 0, 0},
222 {"%s\\test file.shlexec.", 0, 0},
223 {"%s/test file.shlexec", 0, 0},
225 /* Test filenames with no association */
226 {"%s\\test file.noassoc", SE_ERR_NOASSOC, 0},
228 /* Test double extensions */
229 {"%s\\test file.noassoc.shlexec", 0, 0},
230 {"%s\\test file.shlexec.noassoc", SE_ERR_NOASSOC, 0},
233 {"%s\\test_shortcut_shlexec.lnk", 0, 0},
238 static void test_filename()
240 char filename[MAX_PATH];
241 const filename_tests_t* test;
243 DLLVERSIONINFO dllver;
244 HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
249 while (test->basename)
251 sprintf(filename, test->basename, tmpdir);
252 if (strchr(filename, '/'))
262 rc=shell_execute(NULL, filename, NULL, NULL);
269 ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
275 ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
285 ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
290 ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
296 hdll=GetModuleHandleA("shell32.dll");
297 pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
300 dllver.cbSize=sizeof(dllver);
301 pDllGetVersion(&dllver);
302 trace("major=%ld minor=%ld build=%ld platform=%ld\n",
303 dllver.dwMajorVersion, dllver.dwMinorVersion,
304 dllver.dwBuildNumber, dllver.dwPlatformID);
306 /* The more recent versions of shell32.dll accept quoted filenames
307 * while older ones (e.g. 4.00) don't. Still we want to test this
308 * because IE 6 depends on the new behavior.
309 * One day we may need to check the exact version of the dll but for
310 * now making sure DllGetVersion() is present is sufficient.
312 sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
313 rc=shell_execute(NULL, filename, NULL, NULL);
314 ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
317 if (dllver.dwMajorVersion>=6)
319 /* Recent versions of shell32.dll accept '/'s in shortcut paths.
320 * Older versions don't or are quite buggy in this regard.
322 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
330 rc=shell_execute(NULL, filename, NULL, NULL);
332 ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
340 static void test_exes()
342 char filename[MAX_PATH];
345 /* We need NOZONECHECKS on Win2003 to block a dialog */
346 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, "shlexec -nop",
348 ok(rc>=32, "%s returned %d\n", shell_call, rc);
350 sprintf(filename, "%s\\test file.noassoc", tmpdir);
351 if (CopyFile(argv0, filename, FALSE))
353 rc=shell_execute(NULL, filename, "shlexec -nop", NULL);
355 ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
361 static void init_test()
363 char filename[MAX_PATH];
364 WCHAR lnkfile[MAX_PATH];
365 const char* const * testfile;
370 r = CoInitialize(NULL);
371 ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
375 rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
376 assert(rc!=0 && rc<sizeof(argv0));
377 if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
379 strcat(argv0, ".so");
380 ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
381 "unable to find argv0!\n");
384 GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
386 /* Set up the test files */
392 sprintf(filename, *testfile, tmpdir);
393 hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
394 FILE_ATTRIBUTE_NORMAL, NULL);
395 if (hfile==INVALID_HANDLE_VALUE)
397 trace("unable to create '%s': err=%ld\n", filename, GetLastError());
404 /* Setup the test shortcuts */
405 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
406 MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
407 desc.description=NULL;
409 sprintf(filename, "%s\\test file.shlexec", tmpdir);
417 create_lnk(lnkfile, &desc, 0);
419 /* Create a basic association suitable for most tests */
420 create_test_association(".shlexec");
421 create_test_verb(".shlexec", "Open");
424 static void cleanup_test()
426 char filename[MAX_PATH];
427 const char* const * testfile;
429 /* Delete the test files */
433 sprintf(filename, *testfile, tmpdir);
434 DeleteFile(filename);
438 /* Delete the test association */
439 delete_test_association(".shlexec");
447 myARGC = winetest_get_mainargs(&myARGV);
450 /* FIXME: We should dump the parameters we got
451 * and have the parent verify them