shell32/tests: MSDN says ShellExecute() == 32 indicates an error. So fix the tests.
[wine/wine64.git] / dlls / shell32 / tests / shlexec.c
blobadd86484577b7295ba1a219cf243d80591b01543
1 /*
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 /* TODO:
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
27 * in the PATH
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
32 * we could check
35 #include <stdio.h>
36 #include <assert.h>
38 #include "wtypes.h"
39 #include "winbase.h"
40 #include "windef.h"
41 #include "shellapi.h"
42 #include "shlwapi.h"
43 #include "wine/test.h"
45 #include "shell32_test.h"
48 static char argv0[MAX_PATH];
49 static int myARGC;
50 static char** myARGV;
51 static char tmpdir[MAX_PATH];
52 static char child_file[MAX_PATH];
53 static DLLVERSIONINFO dllver;
56 /***
58 * ShellExecute wrappers
60 ***/
61 static void dump_child(void);
63 static HANDLE hEvent;
64 static void init_event(const char* child_file)
66 char* event_name;
67 event_name=strrchr(child_file, '\\')+1;
68 hEvent=CreateEvent(NULL, FALSE, FALSE, event_name);
71 static void strcat_param(char* str, const char* param)
73 if (param!=NULL)
75 strcat(str, "\"");
76 strcat(str, param);
77 strcat(str, "\"");
79 else
81 strcat(str, "null");
85 static char shell_call[2048]="";
86 static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
88 int rc;
90 strcpy(shell_call, "ShellExecute(");
91 strcat_param(shell_call, operation);
92 strcat(shell_call, ", ");
93 strcat_param(shell_call, file);
94 strcat(shell_call, ", ");
95 strcat_param(shell_call, parameters);
96 strcat(shell_call, ", ");
97 strcat_param(shell_call, directory);
98 strcat(shell_call, ")");
99 if (winetest_debug > 1)
100 trace("%s\n", shell_call);
102 DeleteFile(child_file);
103 SetLastError(0xcafebabe);
105 /* FIXME: We cannot use ShellExecuteEx() here because if there is no
106 * association it displays the 'Open With' dialog and I could not find
107 * a flag to prevent this.
109 rc=(int)ShellExecute(NULL, operation, file, parameters, directory,
110 SW_SHOWNORMAL);
112 if (rc > 32)
114 int wait_rc;
115 wait_rc=WaitForSingleObject(hEvent, 5000);
116 ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
118 /* The child process may have changed the result file, so let profile
119 * functions know about it
121 WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
122 if (rc > 32)
123 dump_child();
125 return rc;
128 static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
129 LPCSTR parameters, LPCSTR directory)
131 SHELLEXECUTEINFO sei;
132 BOOL success;
133 int rc;
135 strcpy(shell_call, "ShellExecuteEx(");
136 strcat_param(shell_call, operation);
137 strcat(shell_call, ", ");
138 strcat_param(shell_call, file);
139 strcat(shell_call, ", ");
140 strcat_param(shell_call, parameters);
141 strcat(shell_call, ", ");
142 strcat_param(shell_call, directory);
143 strcat(shell_call, ")");
144 if (winetest_debug > 1)
145 trace("%s\n", shell_call);
147 sei.cbSize=sizeof(sei);
148 sei.fMask=SEE_MASK_NOCLOSEPROCESS | mask;
149 sei.hwnd=NULL;
150 sei.lpVerb=operation;
151 sei.lpFile=file;
152 sei.lpParameters=parameters;
153 sei.lpDirectory=directory;
154 sei.nShow=SW_SHOWNORMAL;
155 sei.hInstApp=NULL; /* Out */
156 sei.lpIDList=NULL;
157 sei.lpClass=NULL;
158 sei.hkeyClass=NULL;
159 sei.dwHotKey=0;
160 U(sei).hIcon=NULL;
161 sei.hProcess=NULL; /* Out */
163 DeleteFile(child_file);
164 SetLastError(0xcafebabe);
165 success=ShellExecuteEx(&sei);
166 rc=(int)sei.hInstApp;
167 ok((success && rc > 32) || (!success && rc <= 32),
168 "%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
170 if (rc > 32)
172 int wait_rc;
173 if (sei.hProcess!=NULL)
175 wait_rc=WaitForSingleObject(sei.hProcess, 5000);
176 ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject(hProcess) returned %d\n", wait_rc);
178 wait_rc=WaitForSingleObject(hEvent, 5000);
179 ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
181 /* The child process may have changed the result file, so let profile
182 * functions know about it
184 WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
185 if (rc > 32)
186 dump_child();
188 return rc;
193 /***
195 * Functions to create / delete associations wrappers
197 ***/
199 static void create_test_association(const char* extension)
201 HKEY hkey, hkey_shell;
202 char class[MAX_PATH];
203 LONG rc;
205 sprintf(class, "shlexec%s", extension);
206 rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
207 NULL, &hkey, NULL);
208 assert(rc==ERROR_SUCCESS);
209 rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, (LPBYTE) class, strlen(class)+1);
210 assert(rc==ERROR_SUCCESS);
211 CloseHandle(hkey);
213 rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
214 KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
215 assert(rc==ERROR_SUCCESS);
216 rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
217 KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
218 assert(rc==ERROR_SUCCESS);
219 CloseHandle(hkey);
220 CloseHandle(hkey_shell);
223 static void delete_test_association(const char* extension)
225 char class[MAX_PATH];
227 sprintf(class, "shlexec%s", extension);
228 SHDeleteKey(HKEY_CLASSES_ROOT, class);
229 SHDeleteKey(HKEY_CLASSES_ROOT, extension);
232 static void create_test_verb(const char* extension, const char* verb,
233 const char* cmdtail)
235 HKEY hkey_shell, hkey_verb, hkey_cmd;
236 char shell[MAX_PATH];
237 char* cmd;
238 LONG rc;
240 sprintf(shell, "shlexec%s\\shell", extension);
241 rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
242 KEY_CREATE_SUB_KEY, &hkey_shell);
243 assert(rc==ERROR_SUCCESS);
244 rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
245 NULL, &hkey_verb, NULL);
246 assert(rc==ERROR_SUCCESS);
247 rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
248 NULL, &hkey_cmd, NULL);
249 assert(rc==ERROR_SUCCESS);
251 cmd=malloc(strlen(argv0)+10+strlen(child_file)+2+strlen(cmdtail)+1);
252 sprintf(cmd,"%s shlexec \"%s\" %s", argv0, child_file, cmdtail);
253 rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE) cmd, strlen(cmd)+1);
254 assert(rc==ERROR_SUCCESS);
256 free(cmd);
257 CloseHandle(hkey_shell);
258 CloseHandle(hkey_verb);
259 CloseHandle(hkey_cmd);
264 /***
266 * Functions to check that the child process was started just right
267 * (borrowed from dlls/kernel32/tests/process.c)
269 ***/
271 static const char* encodeA(const char* str)
273 static char encoded[2*1024+1];
274 char* ptr;
275 size_t len,i;
277 if (!str) return "";
278 len = strlen(str) + 1;
279 if (len >= sizeof(encoded)/2)
281 fprintf(stderr, "string is too long!\n");
282 assert(0);
284 ptr = encoded;
285 for (i = 0; i < len; i++)
286 sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
287 ptr[2 * len] = '\0';
288 return ptr;
291 static unsigned decode_char(char c)
293 if (c >= '0' && c <= '9') return c - '0';
294 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
295 assert(c >= 'A' && c <= 'F');
296 return c - 'A' + 10;
299 static char* decodeA(const char* str)
301 static char decoded[1024];
302 char* ptr;
303 size_t len,i;
305 len = strlen(str) / 2;
306 if (!len--) return NULL;
307 if (len >= sizeof(decoded))
309 fprintf(stderr, "string is too long!\n");
310 assert(0);
312 ptr = decoded;
313 for (i = 0; i < len; i++)
314 ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
315 ptr[len] = '\0';
316 return ptr;
319 static void childPrintf(HANDLE h, const char* fmt, ...)
321 va_list valist;
322 char buffer[1024];
323 DWORD w;
325 va_start(valist, fmt);
326 vsprintf(buffer, fmt, valist);
327 va_end(valist);
328 WriteFile(h, buffer, strlen(buffer), &w, NULL);
331 static void doChild(int argc, char** argv)
333 char* filename;
334 HANDLE hFile;
335 int i;
337 filename=argv[2];
338 hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
339 if (hFile == INVALID_HANDLE_VALUE)
340 return;
342 /* Arguments */
343 childPrintf(hFile, "[Arguments]\n");
344 if (winetest_debug > 2)
345 trace("argcA=%d\n", argc);
346 childPrintf(hFile, "argcA=%d\n", argc);
347 for (i = 0; i < argc; i++)
349 if (winetest_debug > 2)
350 trace("argvA%d=%s\n", i, argv[i]);
351 childPrintf(hFile, "argvA%d=%s\n", i, encodeA(argv[i]));
353 CloseHandle(hFile);
355 init_event(filename);
356 SetEvent(hEvent);
357 CloseHandle(hEvent);
360 static char* getChildString(const char* sect, const char* key)
362 char buf[1024];
363 char* ret;
365 GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file);
366 if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
367 assert(!(strlen(buf) & 1));
368 ret = decodeA(buf);
369 return ret;
372 static void dump_child(void)
374 if (winetest_debug > 1)
376 char key[18];
377 char* str;
378 int i, c;
380 c=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file);
381 trace("argcA=%d\n",c);
382 for (i=0;i<c;i++)
384 sprintf(key, "argvA%d", i);
385 str=getChildString("Arguments", key);
386 trace("%s=%s\n", key, str);
391 static int StrCmpPath(const char* s1, const char* s2)
393 if (!s1 && !s2) return 0;
394 if (!s2) return 1;
395 if (!s1) return -1;
396 while (*s1)
398 if (!*s2)
400 if (*s1=='.')
401 s1++;
402 return (*s1-*s2);
404 if ((*s1=='/' || *s1=='\\') && (*s2=='/' || *s2=='\\'))
406 while (*s1=='/' || *s1=='\\')
407 s1++;
408 while (*s2=='/' || *s2=='\\')
409 s2++;
411 else if (toupper(*s1)==toupper(*s2))
413 s1++;
414 s2++;
416 else
418 return (*s1-*s2);
421 if (*s2=='.')
422 s2++;
423 if (*s2)
424 return -1;
425 return 0;
428 static int _okChildString(const char* file, int line, const char* key, const char* expected)
430 char* result;
431 result=getChildString("Arguments", key);
432 return ok_(file, line)(lstrcmpiA(result, expected) == 0,
433 "%s expected '%s', got '%s'\n", key, expected, result);
436 static int _okChildPath(const char* file, int line, const char* key, const char* expected)
438 char* result;
439 result=getChildString("Arguments", key);
440 return ok_(file, line)(StrCmpPath(result, expected) == 0,
441 "%s expected '%s', got '%s'\n", key, expected, result);
444 static int _okChildInt(const char* file, int line, const char* key, int expected)
446 INT result;
447 result=GetPrivateProfileIntA("Arguments", key, expected, child_file);
448 return ok_(file, line)(result == expected,
449 "%s expected %d, but got %d\n", key, expected, result);
452 #define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
453 #define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
454 #define okChildInt(key, expected) _okChildInt(__FILE__, __LINE__, (key), (expected))
458 /***
460 * Tests
462 ***/
464 static const char* testfiles[]=
466 "%s\\test file.shlexec",
467 "%s\\%%nasty%% $file.shlexec",
468 "%s\\test file.noassoc",
469 "%s\\test file.noassoc.shlexec",
470 "%s\\test file.shlexec.noassoc",
471 "%s\\test_shortcut_shlexec.lnk",
472 "%s\\test_shortcut_exe.lnk",
473 NULL
476 typedef struct
478 const char* verb;
479 const char* basename;
480 int todo;
481 int rc;
482 } filename_tests_t;
484 static filename_tests_t filename_tests[]=
486 /* Test bad / nonexistent filenames */
487 {NULL, "%s\\nonexistent.shlexec", 0x1, ERROR_FILE_NOT_FOUND},
488 {NULL, "%s\\nonexistent.noassoc", 0x1, ERROR_FILE_NOT_FOUND},
490 /* Standard tests */
491 {NULL, "%s\\test file.shlexec", 0x0, 33},
492 {NULL, "%s\\test file.shlexec.", 0x0, 33},
493 {NULL, "%s\\%%nasty%% $file.shlexec", 0x0, 33},
494 {NULL, "%s/test file.shlexec", 0x0, 33},
496 /* Test filenames with no association */
497 {NULL, "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC},
499 /* Test double extensions */
500 {NULL, "%s\\test file.noassoc.shlexec", 0x0, 33},
501 {NULL, "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC},
503 /* Test alternate verbs */
504 {"LowerL", "%s\\nonexistent.shlexec", 0x1, ERROR_FILE_NOT_FOUND},
505 {"LowerL", "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC},
507 {"QuotedLowerL", "%s\\test file.shlexec", 0x0, 33},
508 {"QuotedUpperL", "%s\\test file.shlexec", 0x0, 33},
510 {NULL, NULL, 0}
513 static filename_tests_t noquotes_tests[]=
515 /* Test unquoted '%1' thingies */
516 {"NoQuotes", "%s\\test file.shlexec", 0xa, 33},
517 {"LowerL", "%s\\test file.shlexec", 0xa, 33},
518 {"UpperL", "%s\\test file.shlexec", 0xa, 33},
520 {NULL, NULL, 0}
523 static void test_filename(void)
525 char filename[MAX_PATH];
526 const filename_tests_t* test;
527 char* c;
528 int rc;
530 test=filename_tests;
531 while (test->basename)
533 sprintf(filename, test->basename, tmpdir);
534 if (strchr(filename, '/'))
536 c=filename;
537 while (*c)
539 if (*c=='\\')
540 *c='/';
541 c++;
544 rc=shell_execute(test->verb, filename, NULL, NULL);
545 if (rc > 32)
546 rc=33;
547 if ((test->todo & 0x1)==0)
549 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
550 rc, GetLastError());
552 else todo_wine
554 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
555 rc, GetLastError());
557 if (rc == 33)
559 const char* verb;
560 if ((test->todo & 0x2)==0)
562 okChildInt("argcA", 5);
564 else todo_wine
566 okChildInt("argcA", 5);
568 verb=(test->verb ? test->verb : "Open");
569 if ((test->todo & 0x4)==0)
571 okChildString("argvA3", verb);
573 else todo_wine
575 okChildString("argvA3", verb);
577 if ((test->todo & 0x8)==0)
579 okChildPath("argvA4", filename);
581 else todo_wine
583 okChildPath("argvA4", filename);
586 test++;
589 test=noquotes_tests;
590 while (test->basename)
592 sprintf(filename, test->basename, tmpdir);
593 rc=shell_execute(test->verb, filename, NULL, NULL);
594 if (rc > 32)
595 rc=33;
596 if ((test->todo & 0x1)==0)
598 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
599 rc, GetLastError());
601 else todo_wine
603 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
604 rc, GetLastError());
606 if (rc==0)
608 int count;
609 const char* verb;
610 char* str;
612 verb=(test->verb ? test->verb : "Open");
613 if ((test->todo & 0x4)==0)
615 okChildString("argvA3", verb);
617 else todo_wine
619 okChildString("argvA3", verb);
622 count=4;
623 str=filename;
624 while (1)
626 char attrib[18];
627 char* space;
628 space=strchr(str, ' ');
629 if (space)
630 *space='\0';
631 sprintf(attrib, "argvA%d", count);
632 if ((test->todo & 0x8)==0)
634 okChildPath(attrib, str);
636 else todo_wine
638 okChildPath(attrib, str);
640 count++;
641 if (!space)
642 break;
643 str=space+1;
645 if ((test->todo & 0x2)==0)
647 okChildInt("argcA", count);
649 else todo_wine
651 okChildInt("argcA", count);
654 test++;
657 if (dllver.dwMajorVersion != 0)
659 /* The more recent versions of shell32.dll accept quoted filenames
660 * while older ones (e.g. 4.00) don't. Still we want to test this
661 * because IE 6 depends on the new behavior.
662 * One day we may need to check the exact version of the dll but for
663 * now making sure DllGetVersion() is present is sufficient.
665 sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
666 rc=shell_execute(NULL, filename, NULL, NULL);
667 ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
668 GetLastError());
669 okChildInt("argcA", 5);
670 okChildString("argvA3", "Open");
671 sprintf(filename, "%s\\test file.shlexec", tmpdir);
672 okChildPath("argvA4", filename);
677 static filename_tests_t lnk_tests[]=
679 /* Pass bad / nonexistent filenames as a parameter */
680 {NULL, "%s\\nonexistent.shlexec", 0xa, 33},
681 {NULL, "%s\\nonexistent.noassoc", 0xa, 33},
683 /* Pass regular paths as a parameter */
684 {NULL, "%s\\test file.shlexec", 0xa, 33},
685 {NULL, "%s/%%nasty%% $file.shlexec", 0xa, 33},
687 /* Pass filenames with no association as a parameter */
688 {NULL, "%s\\test file.noassoc", 0xa, 33},
690 {NULL, NULL, 0}
693 static void test_lnks(void)
695 char filename[MAX_PATH];
696 char params[MAX_PATH];
697 const filename_tests_t* test;
698 int rc;
700 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
701 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
702 ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
703 GetLastError());
704 okChildInt("argcA", 5);
705 okChildString("argvA3", "Open");
706 sprintf(filename, "%s\\test file.shlexec", tmpdir);
707 okChildPath("argvA4", filename);
709 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
710 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
711 ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
712 GetLastError());
713 okChildInt("argcA", 4);
714 okChildString("argvA3", "Lnk");
716 if (dllver.dwMajorVersion>=6)
718 char* c;
719 /* Recent versions of shell32.dll accept '/'s in shortcut paths.
720 * Older versions don't or are quite buggy in this regard.
722 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
723 c=filename;
724 while (*c)
726 if (*c=='\\')
727 *c='/';
728 c++;
730 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
731 ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
732 GetLastError());
733 okChildInt("argcA", 4);
734 okChildString("argvA3", "Lnk");
737 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
738 test=lnk_tests;
739 while (test->basename)
741 params[0]='\"';
742 sprintf(params+1, test->basename, tmpdir);
743 strcat(params,"\"");
744 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, params,
745 NULL);
746 if (rc > 32)
747 rc=33;
748 if ((test->todo & 0x1)==0)
750 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
751 rc, GetLastError());
753 else todo_wine
755 ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
756 rc, GetLastError());
758 if (rc==0)
760 if ((test->todo & 0x2)==0)
762 okChildInt("argcA", 5);
764 else
766 okChildInt("argcA", 5);
768 if ((test->todo & 0x4)==0)
770 okChildString("argvA3", "Lnk");
772 else todo_wine
774 okChildString("argvA3", "Lnk");
776 sprintf(params, test->basename, tmpdir);
777 if ((test->todo & 0x8)==0)
779 okChildPath("argvA4", params);
781 else
783 okChildPath("argvA4", params);
786 test++;
791 static void test_exes(void)
793 char filename[MAX_PATH];
794 char params[1024];
795 int rc;
797 sprintf(params, "shlexec \"%s\" Exec", child_file);
799 /* We need NOZONECHECKS on Win2003 to block a dialog */
800 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
801 NULL);
802 ok(rc > 32, "%s returned %d\n", shell_call, rc);
803 okChildInt("argcA", 4);
804 okChildString("argvA3", "Exec");
806 sprintf(filename, "%s\\test file.noassoc", tmpdir);
807 if (CopyFile(argv0, filename, FALSE))
809 rc=shell_execute(NULL, filename, params, NULL);
810 todo_wine {
811 ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
816 static void test_exes_long(void)
818 char filename[MAX_PATH];
819 char params[2024];
820 char longparam[MAX_PATH];
821 int rc;
823 for (rc = 0; rc < MAX_PATH; rc++)
824 longparam[rc]='a'+rc%26;
825 longparam[MAX_PATH-1]=0;
828 sprintf(params, "shlexec \"%s\" %s", child_file,longparam);
830 /* We need NOZONECHECKS on Win2003 to block a dialog */
831 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
832 NULL);
833 ok(rc > 32, "%s returned %d\n", shell_call, rc);
834 okChildInt("argcA", 4);
835 okChildString("argvA3", longparam);
837 sprintf(filename, "%s\\test file.noassoc", tmpdir);
838 if (CopyFile(argv0, filename, FALSE))
840 rc=shell_execute(NULL, filename, params, NULL);
841 todo_wine {
842 ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
848 static void init_test(void)
850 HMODULE hdll;
851 HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
852 char filename[MAX_PATH];
853 WCHAR lnkfile[MAX_PATH];
854 char params[1024];
855 const char* const * testfile;
856 lnk_desc_t desc;
857 DWORD rc;
858 HRESULT r;
860 hdll=GetModuleHandleA("shell32.dll");
861 pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
862 if (pDllGetVersion)
864 dllver.cbSize=sizeof(dllver);
865 pDllGetVersion(&dllver);
866 trace("major=%d minor=%d build=%d platform=%d\n",
867 dllver.dwMajorVersion, dllver.dwMinorVersion,
868 dllver.dwBuildNumber, dllver.dwPlatformID);
870 else
872 memset(&dllver, 0, sizeof(dllver));
875 r = CoInitialize(NULL);
876 ok(SUCCEEDED(r), "CoInitialize failed (0x%08x)\n", r);
877 if (!SUCCEEDED(r))
878 exit(1);
880 rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
881 assert(rc!=0 && rc<sizeof(argv0));
882 if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
884 strcat(argv0, ".so");
885 ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
886 "unable to find argv0!\n");
889 GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
890 assert(GetTempFileNameA(tmpdir, "wt", 0, child_file)!=0);
891 init_event(child_file);
893 /* Set up the test files */
894 testfile=testfiles;
895 while (*testfile)
897 HANDLE hfile;
899 sprintf(filename, *testfile, tmpdir);
900 hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
901 FILE_ATTRIBUTE_NORMAL, NULL);
902 if (hfile==INVALID_HANDLE_VALUE)
904 trace("unable to create '%s': err=%d\n", filename, GetLastError());
905 assert(0);
907 CloseHandle(hfile);
908 testfile++;
911 /* Setup the test shortcuts */
912 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
913 MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
914 desc.description=NULL;
915 desc.workdir=NULL;
916 sprintf(filename, "%s\\test file.shlexec", tmpdir);
917 desc.path=filename;
918 desc.pidl=NULL;
919 desc.arguments="ignored";
920 desc.showcmd=0;
921 desc.icon=NULL;
922 desc.icon_id=0;
923 desc.hotkey=0;
924 create_lnk(lnkfile, &desc, 0);
926 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
927 MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
928 desc.description=NULL;
929 desc.workdir=NULL;
930 desc.path=argv0;
931 desc.pidl=NULL;
932 sprintf(params, "shlexec \"%s\" Lnk", child_file);
933 desc.arguments=params;
934 desc.showcmd=0;
935 desc.icon=NULL;
936 desc.icon_id=0;
937 desc.hotkey=0;
938 create_lnk(lnkfile, &desc, 0);
940 /* Create a basic association suitable for most tests */
941 create_test_association(".shlexec");
942 create_test_verb(".shlexec", "Open", "Open \"%1\"");
943 create_test_verb(".shlexec", "NoQuotes", "NoQuotes %1");
944 create_test_verb(".shlexec", "LowerL", "LowerL %l");
945 create_test_verb(".shlexec", "QuotedLowerL", "QuotedLowerL \"%l\"");
946 create_test_verb(".shlexec", "UpperL", "UpperL %L");
947 create_test_verb(".shlexec", "QuotedUpperL", "QuotedUpperL \"%L\"");
950 static void cleanup_test(void)
952 char filename[MAX_PATH];
953 const char* const * testfile;
955 /* Delete the test files */
956 testfile=testfiles;
957 while (*testfile)
959 sprintf(filename, *testfile, tmpdir);
960 DeleteFile(filename);
961 testfile++;
963 DeleteFile(child_file);
965 /* Delete the test association */
966 delete_test_association(".shlexec");
968 CloseHandle(hEvent);
970 CoUninitialize();
973 START_TEST(shlexec)
976 myARGC = winetest_get_mainargs(&myARGV);
977 if (myARGC >= 3)
979 doChild(myARGC, myARGV);
980 exit(0);
983 init_test();
985 test_filename();
986 test_lnks();
987 test_exes();
988 test_exes_long();
990 cleanup_test();