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
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
38 /* Needed to get SEE_MASK_NOZONECHECKS with the PSDK */
39 #define NTDDI_WINXPSP1 0x05010100
40 #define NTDDI_VERSION NTDDI_WINXPSP1
47 #include "wine/test.h"
49 #include "shell32_test.h"
52 static char argv0
[MAX_PATH
];
55 static char tmpdir
[MAX_PATH
];
56 static char child_file
[MAX_PATH
];
57 static DLLVERSIONINFO dllver
;
62 * ShellExecute wrappers
65 static void dump_child(void);
68 static void init_event(const char* child_file
)
71 event_name
=strrchr(child_file
, '\\')+1;
72 hEvent
=CreateEvent(NULL
, FALSE
, FALSE
, event_name
);
75 static void strcat_param(char* str
, const char* param
)
89 static char shell_call
[2048]="";
90 static int shell_execute(LPCSTR operation
, LPCSTR file
, LPCSTR parameters
, LPCSTR directory
)
94 strcpy(shell_call
, "ShellExecute(");
95 strcat_param(shell_call
, operation
);
96 strcat(shell_call
, ", ");
97 strcat_param(shell_call
, file
);
98 strcat(shell_call
, ", ");
99 strcat_param(shell_call
, parameters
);
100 strcat(shell_call
, ", ");
101 strcat_param(shell_call
, directory
);
102 strcat(shell_call
, ")");
103 if (winetest_debug
> 1)
104 trace("%s\n", shell_call
);
106 DeleteFile(child_file
);
107 SetLastError(0xcafebabe);
109 /* FIXME: We cannot use ShellExecuteEx() here because if there is no
110 * association it displays the 'Open With' dialog and I could not find
111 * a flag to prevent this.
113 rc
=(int)ShellExecute(NULL
, operation
, file
, parameters
, directory
,
119 wait_rc
=WaitForSingleObject(hEvent
, 5000);
120 ok(wait_rc
==WAIT_OBJECT_0
, "WaitForSingleObject returned %d\n", wait_rc
);
122 /* The child process may have changed the result file, so let profile
123 * functions know about it
125 WritePrivateProfileStringA(NULL
, NULL
, NULL
, child_file
);
132 static int shell_execute_ex(DWORD mask
, LPCSTR operation
, LPCSTR file
,
133 LPCSTR parameters
, LPCSTR directory
)
135 SHELLEXECUTEINFO sei
;
139 strcpy(shell_call
, "ShellExecuteEx(");
140 strcat_param(shell_call
, operation
);
141 strcat(shell_call
, ", ");
142 strcat_param(shell_call
, file
);
143 strcat(shell_call
, ", ");
144 strcat_param(shell_call
, parameters
);
145 strcat(shell_call
, ", ");
146 strcat_param(shell_call
, directory
);
147 strcat(shell_call
, ")");
148 if (winetest_debug
> 1)
149 trace("%s\n", shell_call
);
151 sei
.cbSize
=sizeof(sei
);
152 sei
.fMask
=SEE_MASK_NOCLOSEPROCESS
| mask
;
154 sei
.lpVerb
=operation
;
156 sei
.lpParameters
=parameters
;
157 sei
.lpDirectory
=directory
;
158 sei
.nShow
=SW_SHOWNORMAL
;
159 sei
.hInstApp
=NULL
; /* Out */
165 sei
.hProcess
=NULL
; /* Out */
167 DeleteFile(child_file
);
168 SetLastError(0xcafebabe);
169 success
=ShellExecuteEx(&sei
);
170 rc
=(int)sei
.hInstApp
;
171 ok((success
&& rc
> 32) || (!success
&& rc
<= 32),
172 "%s rc=%d and hInstApp=%d is not allowed\n", shell_call
, success
, rc
);
177 if (sei
.hProcess
!=NULL
)
179 wait_rc
=WaitForSingleObject(sei
.hProcess
, 5000);
180 ok(wait_rc
==WAIT_OBJECT_0
, "WaitForSingleObject(hProcess) returned %d\n", wait_rc
);
182 wait_rc
=WaitForSingleObject(hEvent
, 5000);
183 ok(wait_rc
==WAIT_OBJECT_0
, "WaitForSingleObject returned %d\n", wait_rc
);
185 /* The child process may have changed the result file, so let profile
186 * functions know about it
188 WritePrivateProfileStringA(NULL
, NULL
, NULL
, child_file
);
199 * Functions to create / delete associations wrappers
203 static void create_test_association(const char* extension
)
205 HKEY hkey
, hkey_shell
;
206 char class[MAX_PATH
];
209 sprintf(class, "shlexec%s", extension
);
210 rc
=RegCreateKeyEx(HKEY_CLASSES_ROOT
, extension
, 0, NULL
, 0, KEY_SET_VALUE
,
212 assert(rc
==ERROR_SUCCESS
);
213 rc
=RegSetValueEx(hkey
, NULL
, 0, REG_SZ
, (LPBYTE
) class, strlen(class)+1);
214 assert(rc
==ERROR_SUCCESS
);
217 rc
=RegCreateKeyEx(HKEY_CLASSES_ROOT
, class, 0, NULL
, 0,
218 KEY_CREATE_SUB_KEY
| KEY_ENUMERATE_SUB_KEYS
, NULL
, &hkey
, NULL
);
219 assert(rc
==ERROR_SUCCESS
);
220 rc
=RegCreateKeyEx(hkey
, "shell", 0, NULL
, 0,
221 KEY_CREATE_SUB_KEY
, NULL
, &hkey_shell
, NULL
);
222 assert(rc
==ERROR_SUCCESS
);
224 CloseHandle(hkey_shell
);
227 static void delete_test_association(const char* extension
)
229 char class[MAX_PATH
];
231 sprintf(class, "shlexec%s", extension
);
232 SHDeleteKey(HKEY_CLASSES_ROOT
, class);
233 SHDeleteKey(HKEY_CLASSES_ROOT
, extension
);
236 static void create_test_verb_dde(const char* extension
, const char* verb
,
237 int rawcmd
, const char* cmdtail
, const char *ddeexec
,
238 const char *application
, const char *topic
,
241 HKEY hkey_shell
, hkey_verb
, hkey_cmd
;
242 char shell
[MAX_PATH
];
246 sprintf(shell
, "shlexec%s\\shell", extension
);
247 rc
=RegOpenKeyEx(HKEY_CLASSES_ROOT
, shell
, 0,
248 KEY_CREATE_SUB_KEY
, &hkey_shell
);
249 assert(rc
==ERROR_SUCCESS
);
250 rc
=RegCreateKeyEx(hkey_shell
, verb
, 0, NULL
, 0, KEY_CREATE_SUB_KEY
,
251 NULL
, &hkey_verb
, NULL
);
252 assert(rc
==ERROR_SUCCESS
);
253 rc
=RegCreateKeyEx(hkey_verb
, "command", 0, NULL
, 0, KEY_SET_VALUE
,
254 NULL
, &hkey_cmd
, NULL
);
255 assert(rc
==ERROR_SUCCESS
);
259 rc
=RegSetValueEx(hkey_cmd
, NULL
, 0, REG_SZ
, (LPBYTE
)cmdtail
, strlen(cmdtail
)+1);
263 cmd
=malloc(strlen(argv0
)+10+strlen(child_file
)+2+strlen(cmdtail
)+1);
264 sprintf(cmd
,"%s shlexec \"%s\" %s", argv0
, child_file
, cmdtail
);
265 rc
=RegSetValueEx(hkey_cmd
, NULL
, 0, REG_SZ
, (LPBYTE
)cmd
, strlen(cmd
)+1);
266 assert(rc
==ERROR_SUCCESS
);
272 HKEY hkey_ddeexec
, hkey_application
, hkey_topic
, hkey_ifexec
;
274 rc
=RegCreateKeyEx(hkey_verb
, "ddeexec", 0, NULL
, 0, KEY_SET_VALUE
|
275 KEY_CREATE_SUB_KEY
, NULL
, &hkey_ddeexec
, NULL
);
276 assert(rc
==ERROR_SUCCESS
);
277 rc
=RegSetValueEx(hkey_ddeexec
, NULL
, 0, REG_SZ
, (LPBYTE
)ddeexec
,
279 assert(rc
==ERROR_SUCCESS
);
282 rc
=RegCreateKeyEx(hkey_ddeexec
, "application", 0, NULL
, 0, KEY_SET_VALUE
,
283 NULL
, &hkey_application
, NULL
);
284 assert(rc
==ERROR_SUCCESS
);
285 rc
=RegSetValueEx(hkey_application
, NULL
, 0, REG_SZ
, (LPBYTE
)application
,
286 strlen(application
)+1);
287 assert(rc
==ERROR_SUCCESS
);
288 CloseHandle(hkey_application
);
292 rc
=RegCreateKeyEx(hkey_ddeexec
, "topic", 0, NULL
, 0, KEY_SET_VALUE
,
293 NULL
, &hkey_topic
, NULL
);
294 assert(rc
==ERROR_SUCCESS
);
295 rc
=RegSetValueEx(hkey_topic
, NULL
, 0, REG_SZ
, (LPBYTE
)topic
,
297 assert(rc
==ERROR_SUCCESS
);
298 CloseHandle(hkey_topic
);
302 rc
=RegCreateKeyEx(hkey_ddeexec
, "ifexec", 0, NULL
, 0, KEY_SET_VALUE
,
303 NULL
, &hkey_ifexec
, NULL
);
304 assert(rc
==ERROR_SUCCESS
);
305 rc
=RegSetValueEx(hkey_ifexec
, NULL
, 0, REG_SZ
, (LPBYTE
)ifexec
,
307 assert(rc
==ERROR_SUCCESS
);
308 CloseHandle(hkey_ifexec
);
310 CloseHandle(hkey_ddeexec
);
313 CloseHandle(hkey_shell
);
314 CloseHandle(hkey_verb
);
315 CloseHandle(hkey_cmd
);
318 static void create_test_verb(const char* extension
, const char* verb
,
319 int rawcmd
, const char* cmdtail
)
321 create_test_verb_dde(extension
, verb
, rawcmd
, cmdtail
, NULL
, NULL
,
327 * Functions to check that the child process was started just right
328 * (borrowed from dlls/kernel32/tests/process.c)
332 static const char* encodeA(const char* str
)
334 static char encoded
[2*1024+1];
339 len
= strlen(str
) + 1;
340 if (len
>= sizeof(encoded
)/2)
342 fprintf(stderr
, "string is too long!\n");
346 for (i
= 0; i
< len
; i
++)
347 sprintf(&ptr
[i
* 2], "%02x", (unsigned char)str
[i
]);
352 static unsigned decode_char(char c
)
354 if (c
>= '0' && c
<= '9') return c
- '0';
355 if (c
>= 'a' && c
<= 'f') return c
- 'a' + 10;
356 assert(c
>= 'A' && c
<= 'F');
360 static char* decodeA(const char* str
)
362 static char decoded
[1024];
366 len
= strlen(str
) / 2;
367 if (!len
--) return NULL
;
368 if (len
>= sizeof(decoded
))
370 fprintf(stderr
, "string is too long!\n");
374 for (i
= 0; i
< len
; i
++)
375 ptr
[i
] = (decode_char(str
[2 * i
]) << 4) | decode_char(str
[2 * i
+ 1]);
380 static void childPrintf(HANDLE h
, const char* fmt
, ...)
386 va_start(valist
, fmt
);
387 vsprintf(buffer
, fmt
, valist
);
389 WriteFile(h
, buffer
, strlen(buffer
), &w
, NULL
);
392 static void doChild(int argc
, char** argv
)
399 hFile
=CreateFileA(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, 0);
400 if (hFile
== INVALID_HANDLE_VALUE
)
404 childPrintf(hFile
, "[Arguments]\r\n");
405 if (winetest_debug
> 2)
406 trace("argcA=%d\n", argc
);
407 childPrintf(hFile
, "argcA=%d\r\n", argc
);
408 for (i
= 0; i
< argc
; i
++)
410 if (winetest_debug
> 2)
411 trace("argvA%d=%s\n", i
, argv
[i
]);
412 childPrintf(hFile
, "argvA%d=%s\r\n", i
, encodeA(argv
[i
]));
416 init_event(filename
);
421 static char* getChildString(const char* sect
, const char* key
)
426 GetPrivateProfileStringA(sect
, key
, "-", buf
, sizeof(buf
), child_file
);
427 if (buf
[0] == '\0' || (buf
[0] == '-' && buf
[1] == '\0')) return NULL
;
428 assert(!(strlen(buf
) & 1));
433 static void dump_child(void)
435 if (winetest_debug
> 1)
441 c
=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file
);
442 trace("argcA=%d\n",c
);
445 sprintf(key
, "argvA%d", i
);
446 str
=getChildString("Arguments", key
);
447 trace("%s=%s\n", key
, str
);
452 static int StrCmpPath(const char* s1
, const char* s2
)
454 if (!s1
&& !s2
) return 0;
465 if ((*s1
=='/' || *s1
=='\\') && (*s2
=='/' || *s2
=='\\'))
467 while (*s1
=='/' || *s1
=='\\')
469 while (*s2
=='/' || *s2
=='\\')
472 else if (toupper(*s1
)==toupper(*s2
))
489 static int _okChildString(const char* file
, int line
, const char* key
, const char* expected
)
492 result
=getChildString("Arguments", key
);
493 return ok_(file
, line
)(lstrcmpiA(result
, expected
) == 0,
494 "%s expected '%s', got '%s'\n", key
, expected
, result
);
497 static int _okChildPath(const char* file
, int line
, const char* key
, const char* expected
)
500 result
=getChildString("Arguments", key
);
501 return ok_(file
, line
)(StrCmpPath(result
, expected
) == 0,
502 "%s expected '%s', got '%s'\n", key
, expected
, result
);
505 static int _okChildInt(const char* file
, int line
, const char* key
, int expected
)
508 result
=GetPrivateProfileIntA("Arguments", key
, expected
, child_file
);
509 return ok_(file
, line
)(result
== expected
,
510 "%s expected %d, but got %d\n", key
, expected
, result
);
513 #define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
514 #define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
515 #define okChildInt(key, expected) _okChildInt(__FILE__, __LINE__, (key), (expected))
525 static const char* testfiles
[]=
527 "%s\\test file.shlexec",
528 "%s\\%%nasty%% $file.shlexec",
529 "%s\\test file.noassoc",
530 "%s\\test file.noassoc.shlexec",
531 "%s\\test file.shlexec.noassoc",
532 "%s\\test_shortcut_shlexec.lnk",
533 "%s\\test_shortcut_exe.lnk",
535 "%s\\test file.shlfoo",
537 "%s\\masked file.shlexec",
548 const char* basename
;
553 static filename_tests_t filename_tests
[]=
555 /* Test bad / nonexistent filenames */
556 {NULL
, "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF
},
557 {NULL
, "%s\\nonexistent.noassoc", 0x11, SE_ERR_FNF
},
560 {NULL
, "%s\\test file.shlexec", 0x0, 33},
561 {NULL
, "%s\\test file.shlexec.", 0x0, 33},
562 {NULL
, "%s\\%%nasty%% $file.shlexec", 0x0, 33},
563 {NULL
, "%s/test file.shlexec", 0x0, 33},
565 /* Test filenames with no association */
566 {NULL
, "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC
},
568 /* Test double extensions */
569 {NULL
, "%s\\test file.noassoc.shlexec", 0x0, 33},
570 {NULL
, "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC
},
572 /* Test alternate verbs */
573 {"LowerL", "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF
},
574 {"LowerL", "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC
},
576 {"QuotedLowerL", "%s\\test file.shlexec", 0x0, 33},
577 {"QuotedUpperL", "%s\\test file.shlexec", 0x0, 33},
579 /* Test file masked due to space */
580 {NULL
, "%s\\masked file.shlexec", 0x1, 33},
581 /* Test if quoting prevents the masking */
582 {NULL
, "%s\\masked file.shlexec", 0x40, 33},
587 static filename_tests_t noquotes_tests
[]=
589 /* Test unquoted '%1' thingies */
590 {"NoQuotes", "%s\\test file.shlexec", 0xa, 33},
591 {"LowerL", "%s\\test file.shlexec", 0xa, 33},
592 {"UpperL", "%s\\test file.shlexec", 0xa, 33},
597 static void test_filename(void)
599 char filename
[MAX_PATH
];
600 const filename_tests_t
* test
;
605 while (test
->basename
)
607 sprintf(filename
, test
->basename
, tmpdir
);
608 if (strchr(filename
, '/'))
618 if ((test
->todo
& 0x40)==0)
620 rc
=shell_execute(test
->verb
, filename
, NULL
, NULL
);
624 char quoted
[MAX_PATH
+ 2];
625 sprintf(quoted
, "\"%s\"", filename
);
626 rc
=shell_execute(test
->verb
, quoted
, NULL
, NULL
);
630 if ((test
->todo
& 0x1)==0)
632 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
637 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
643 if ((test
->todo
& 0x2)==0)
645 okChildInt("argcA", 5);
649 okChildInt("argcA", 5);
651 verb
=(test
->verb
? test
->verb
: "Open");
652 if ((test
->todo
& 0x4)==0)
654 okChildString("argvA3", verb
);
658 okChildString("argvA3", verb
);
660 if ((test
->todo
& 0x8)==0)
662 okChildPath("argvA4", filename
);
666 okChildPath("argvA4", filename
);
673 while (test
->basename
)
675 sprintf(filename
, test
->basename
, tmpdir
);
676 rc
=shell_execute(test
->verb
, filename
, NULL
, NULL
);
679 if ((test
->todo
& 0x1)==0)
681 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
686 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
695 verb
=(test
->verb
? test
->verb
: "Open");
696 if ((test
->todo
& 0x4)==0)
698 okChildString("argvA3", verb
);
702 okChildString("argvA3", verb
);
711 space
=strchr(str
, ' ');
714 sprintf(attrib
, "argvA%d", count
);
715 if ((test
->todo
& 0x8)==0)
717 okChildPath(attrib
, str
);
721 okChildPath(attrib
, str
);
728 if ((test
->todo
& 0x2)==0)
730 okChildInt("argcA", count
);
734 okChildInt("argcA", count
);
740 if (dllver
.dwMajorVersion
!= 0)
742 /* The more recent versions of shell32.dll accept quoted filenames
743 * while older ones (e.g. 4.00) don't. Still we want to test this
744 * because IE 6 depends on the new behavior.
745 * One day we may need to check the exact version of the dll but for
746 * now making sure DllGetVersion() is present is sufficient.
748 sprintf(filename
, "\"%s\\test file.shlexec\"", tmpdir
);
749 rc
=shell_execute(NULL
, filename
, NULL
, NULL
);
750 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
752 okChildInt("argcA", 5);
753 okChildString("argvA3", "Open");
754 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
755 okChildPath("argvA4", filename
);
759 static void test_find_executable(void)
761 char filename
[MAX_PATH
];
762 char command
[MAX_PATH
];
763 const filename_tests_t
* test
;
766 create_test_association(".sfe");
767 create_test_verb(".sfe", "Open", 1, "%1");
769 /* Don't test FindExecutable(..., NULL), it always crashes */
771 strcpy(command
, "your word");
772 rc
=(int)FindExecutableA(NULL
, NULL
, command
);
773 ok(rc
== SE_ERR_FNF
|| rc
> 32 /* nt4 */, "FindExecutable(NULL) returned %d\n", rc
);
774 ok(strcmp(command
, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command
);
776 strcpy(command
, "your word");
777 rc
=(int)FindExecutableA(tmpdir
, NULL
, command
);
778 ok(rc
== SE_ERR_NOASSOC
/* >= win2000 */ || rc
> 32 /* win98, nt4 */, "FindExecutable(NULL) returned %d\n", rc
);
779 ok(strcmp(command
, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command
);
781 sprintf(filename
, "%s\\test file.sfe", tmpdir
);
782 rc
=(int)FindExecutableA(filename
, NULL
, command
);
783 ok(rc
> 32, "FindExecutable(%s) returned %d\n", filename
, rc
);
784 /* Depending on the platform, command could be '%1' or 'test file.sfe' */
786 rc
=(int)FindExecutableA("test file.sfe", tmpdir
, command
);
787 ok(rc
> 32, "FindExecutable(%s) returned %d\n", filename
, rc
);
789 rc
=(int)FindExecutableA("test file.sfe", NULL
, command
);
790 todo_wine
ok(rc
== SE_ERR_FNF
, "FindExecutable(%s) returned %d\n", filename
, rc
);
792 delete_test_association(".sfe");
794 create_test_association(".shl");
795 create_test_verb(".shl", "Open", 0, "Open");
797 sprintf(filename
, "%s\\test file.shl", tmpdir
);
798 rc
=(int)FindExecutableA(filename
, NULL
, command
);
799 ok(rc
== SE_ERR_FNF
/* NT4 */ || rc
> 32, "FindExecutable(%s) returned %d\n", filename
, rc
);
801 sprintf(filename
, "%s\\test file.shlfoo", tmpdir
);
802 rc
=(int)FindExecutableA(filename
, NULL
, command
);
804 delete_test_association(".shl");
808 /* On Windows XP and 2003 FindExecutable() is completely broken.
809 * Probably what it does is convert the filename to 8.3 format,
810 * which as a side effect converts the '.shlfoo' extension to '.shl',
811 * and then tries to find an association for '.shl'. This means it
812 * will normally fail on most extensions with more than 3 characters,
814 * Also it means we cannot do any other test.
816 trace("FindExecutable() is broken -> skipping 4+ character extension tests\n");
821 while (test
->basename
)
823 sprintf(filename
, test
->basename
, tmpdir
);
824 if (strchr(filename
, '/'))
835 /* Win98 does not '\0'-terminate command! */
836 memset(command
, '\0', sizeof(command
));
837 rc
=(int)FindExecutableA(filename
, NULL
, command
);
840 if ((test
->todo
& 0x10)==0)
842 ok(rc
==test
->rc
, "FindExecutable(%s) failed: rc=%d\n", filename
, rc
);
846 ok(rc
==test
->rc
, "FindExecutable(%s) failed: rc=%d\n", filename
, rc
);
851 equal
=strcmp(command
, argv0
) == 0 ||
852 /* NT4 returns an extra 0x8 character! */
853 (strlen(command
) == strlen(argv0
)+1 && strncmp(command
, argv0
, strlen(argv0
)) == 0);
854 if ((test
->todo
& 0x20)==0)
856 ok(equal
, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
857 filename
, command
, argv0
);
861 ok(equal
, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
862 filename
, command
, argv0
);
870 static filename_tests_t lnk_tests
[]=
872 /* Pass bad / nonexistent filenames as a parameter */
873 {NULL
, "%s\\nonexistent.shlexec", 0xa, 33},
874 {NULL
, "%s\\nonexistent.noassoc", 0xa, 33},
876 /* Pass regular paths as a parameter */
877 {NULL
, "%s\\test file.shlexec", 0xa, 33},
878 {NULL
, "%s/%%nasty%% $file.shlexec", 0xa, 33},
880 /* Pass filenames with no association as a parameter */
881 {NULL
, "%s\\test file.noassoc", 0xa, 33},
886 static void test_lnks(void)
888 char filename
[MAX_PATH
];
889 char params
[MAX_PATH
];
890 const filename_tests_t
* test
;
893 sprintf(filename
, "%s\\test_shortcut_shlexec.lnk", tmpdir
);
894 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
895 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
897 okChildInt("argcA", 5);
898 okChildString("argvA3", "Open");
899 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
900 okChildPath("argvA4", filename
);
902 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
903 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
904 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
906 okChildInt("argcA", 4);
907 okChildString("argvA3", "Lnk");
909 if (dllver
.dwMajorVersion
>=6)
912 /* Recent versions of shell32.dll accept '/'s in shortcut paths.
913 * Older versions don't or are quite buggy in this regard.
915 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
923 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
924 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
926 okChildInt("argcA", 4);
927 okChildString("argvA3", "Lnk");
930 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
932 while (test
->basename
)
935 sprintf(params
+1, test
->basename
, tmpdir
);
937 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, params
,
941 if ((test
->todo
& 0x1)==0)
943 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
948 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
953 if ((test
->todo
& 0x2)==0)
955 okChildInt("argcA", 5);
959 okChildInt("argcA", 5);
961 if ((test
->todo
& 0x4)==0)
963 okChildString("argvA3", "Lnk");
967 okChildString("argvA3", "Lnk");
969 sprintf(params
, test
->basename
, tmpdir
);
970 if ((test
->todo
& 0x8)==0)
972 okChildPath("argvA4", params
);
976 okChildPath("argvA4", params
);
984 static void test_exes(void)
986 char filename
[MAX_PATH
];
990 sprintf(params
, "shlexec \"%s\" Exec", child_file
);
992 /* We need NOZONECHECKS on Win2003 to block a dialog */
993 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, argv0
, params
,
995 ok(rc
> 32, "%s returned %d\n", shell_call
, rc
);
996 okChildInt("argcA", 4);
997 okChildString("argvA3", "Exec");
999 sprintf(filename
, "%s\\test file.noassoc", tmpdir
);
1000 if (CopyFile(argv0
, filename
, FALSE
))
1002 rc
=shell_execute(NULL
, filename
, params
, NULL
);
1004 ok(rc
==SE_ERR_NOASSOC
, "%s succeeded: rc=%d\n", shell_call
, rc
);
1009 static void test_exes_long(void)
1011 char filename
[MAX_PATH
];
1013 char longparam
[MAX_PATH
];
1016 for (rc
= 0; rc
< MAX_PATH
; rc
++)
1017 longparam
[rc
]='a'+rc
%26;
1018 longparam
[MAX_PATH
-1]=0;
1021 sprintf(params
, "shlexec \"%s\" %s", child_file
,longparam
);
1023 /* We need NOZONECHECKS on Win2003 to block a dialog */
1024 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, argv0
, params
,
1026 ok(rc
> 32, "%s returned %d\n", shell_call
, rc
);
1027 okChildInt("argcA", 4);
1028 okChildString("argvA3", longparam
);
1030 sprintf(filename
, "%s\\test file.noassoc", tmpdir
);
1031 if (CopyFile(argv0
, filename
, FALSE
))
1033 rc
=shell_execute(NULL
, filename
, params
, NULL
);
1035 ok(rc
==SE_ERR_NOASSOC
, "%s succeeded: rc=%d\n", shell_call
, rc
);
1042 const char* command
;
1043 const char* ddeexec
;
1044 const char* application
;
1048 const char* expectedDdeExec
;
1053 static dde_tests_t dde_tests
[] =
1055 /* Test passing and not passing command-line
1056 * argument, no DDE */
1057 {"", NULL
, NULL
, NULL
, NULL
, FALSE
, "", 0x0, 33},
1058 {"\"%1\"", NULL
, NULL
, NULL
, NULL
, TRUE
, "", 0x0, 33},
1060 /* Test passing and not passing command-line
1061 * argument, with DDE */
1062 {"", "[open(\"%1\")]", "shlexec", "dde", NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1063 {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL
, TRUE
, "[open(\"%s\")]", 0x0, 33},
1065 /* Test unquoted %1 in command and ddeexec
1066 * (test filename has space) */
1067 {"%1", "[open(%1)]", "shlexec", "dde", NULL
, 2, "[open(%s)]", 0xa, 33},
1069 /* Test ifexec precedence over ddeexec */
1070 {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE
, "[ifexec(\"%s\")]", 0x0, 33},
1072 /* Test default DDE topic */
1073 {"", "[open(\"%1\")]", "shlexec", NULL
, NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1075 /* Test default DDE application */
1076 {"", "[open(\"%1\")]", NULL
, "dde", NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1078 {NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0x0, 0}
1081 static DWORD ddeInst
;
1082 static HSZ hszTopic
;
1083 static char ddeExec
[MAX_PATH
], ddeApplication
[MAX_PATH
];
1084 static BOOL denyNextConnection
;
1086 static HDDEDATA CALLBACK
ddeCb(UINT uType
, UINT uFmt
, HCONV hConv
,
1087 HSZ hsz1
, HSZ hsz2
, HDDEDATA hData
,
1088 ULONG_PTR dwData1
, ULONG_PTR dwData2
)
1092 if (winetest_debug
> 2)
1093 trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
1094 uType
, uFmt
, hConv
, hsz1
, hsz2
, hData
, dwData1
, dwData2
);
1099 if (!DdeCmpStringHandles(hsz1
, hszTopic
))
1101 if (denyNextConnection
)
1102 denyNextConnection
= FALSE
;
1105 size
= DdeQueryString(ddeInst
, hsz2
, ddeApplication
, MAX_PATH
, CP_WINANSI
);
1106 assert(size
< MAX_PATH
);
1107 return (HDDEDATA
)TRUE
;
1110 return (HDDEDATA
)FALSE
;
1113 size
= DdeGetData(hData
, (LPBYTE
)ddeExec
, MAX_PATH
, 0L);
1114 assert(size
< MAX_PATH
);
1115 DdeFreeDataHandle(hData
);
1116 return (HDDEDATA
)DDE_FACK
;
1126 DWORD threadIdParent
;
1127 } dde_thread_info_t
;
1129 static DWORD CALLBACK
ddeThread(LPVOID arg
)
1131 dde_thread_info_t
*info
= (dde_thread_info_t
*)arg
;
1132 assert(info
&& info
->filename
);
1133 PostThreadMessage(info
->threadIdParent
,
1135 shell_execute_ex(SEE_MASK_FLAG_DDEWAIT
| SEE_MASK_FLAG_NO_UI
, NULL
, info
->filename
, NULL
, NULL
),
1140 /* ShellExecute won't successfully send DDE commands to console applications after starting them,
1141 * so we run a DDE server in this application, deny the first connection request to make
1142 * ShellExecute start the application, and then process the next DDE connection in this application
1143 * to see the execute command that is sent. */
1144 static void test_dde(void)
1146 char filename
[MAX_PATH
], defApplication
[MAX_PATH
];
1148 dde_thread_info_t info
= { filename
, GetCurrentThreadId() };
1149 const dde_tests_t
* test
;
1156 rc
= DdeInitializeA(&ddeInst
, ddeCb
, CBF_SKIP_ALLNOTIFICATIONS
| CBF_FAIL_ADVISES
|
1157 CBF_FAIL_POKES
| CBF_FAIL_REQUESTS
, 0L);
1158 assert(rc
== DMLERR_NO_ERROR
);
1160 sprintf(filename
, "%s\\test file.sde", tmpdir
);
1162 /* Default service is application name minus path and extension */
1163 strcpy(defApplication
, strrchr(argv0
, '\\')+1);
1164 *strchr(defApplication
, '.') = 0;
1167 while (test
->command
)
1169 create_test_association(".sde");
1170 create_test_verb_dde(".sde", "Open", 0, test
->command
, test
->ddeexec
,
1171 test
->application
, test
->topic
, test
->ifexec
);
1172 hszApplication
= DdeCreateStringHandleA(ddeInst
, test
->application
?
1173 test
->application
: defApplication
, CP_WINANSI
);
1174 hszTopic
= DdeCreateStringHandleA(ddeInst
, test
->topic
? test
->topic
: SZDDESYS_TOPIC
,
1176 assert(hszApplication
&& hszTopic
);
1177 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_REGISTER
));
1178 denyNextConnection
= TRUE
;
1181 assert(CreateThread(NULL
, 0, ddeThread
, (LPVOID
)&info
, 0, &threadId
));
1182 while (GetMessage(&msg
, NULL
, 0, 0)) DispatchMessage(&msg
);
1183 rc
= msg
.wParam
> 32 ? 33 : msg
.wParam
;
1184 if ((test
->todo
& 0x1)==0)
1186 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1187 rc
, GetLastError());
1191 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1192 rc
, GetLastError());
1196 if ((test
->todo
& 0x2)==0)
1198 okChildInt("argcA", test
->expectedArgs
+ 3);
1202 okChildInt("argcA", test
->expectedArgs
+ 3);
1204 if (test
->expectedArgs
== 1)
1206 if ((test
->todo
& 0x4) == 0)
1208 okChildPath("argvA3", filename
);
1212 okChildPath("argvA3", filename
);
1215 if ((test
->todo
& 0x8) == 0)
1217 sprintf(params
, test
->expectedDdeExec
, filename
);
1218 ok(StrCmpPath(params
, ddeExec
) == 0,
1219 "ddeexec expected '%s', got '%s'\n", params
, ddeExec
);
1223 sprintf(params
, test
->expectedDdeExec
, filename
);
1224 ok(StrCmpPath(params
, ddeExec
) == 0,
1225 "ddeexec expected '%s', got '%s'\n", params
, ddeExec
);
1229 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_UNREGISTER
));
1230 assert(DdeFreeStringHandle(ddeInst
, hszTopic
));
1231 assert(DdeFreeStringHandle(ddeInst
, hszApplication
));
1232 delete_test_association(".sde");
1236 assert(DdeUninitialize(ddeInst
));
1239 #define DDE_DEFAULT_APP_VARIANTS 2
1242 const char* command
;
1243 const char* expectedDdeApplication
[DDE_DEFAULT_APP_VARIANTS
];
1245 int rc
[DDE_DEFAULT_APP_VARIANTS
];
1246 } dde_default_app_tests_t
;
1248 static dde_default_app_tests_t dde_default_app_tests
[] =
1250 /* Windows XP and 98 handle default DDE app names in different ways.
1251 * The application name we see in the first test determines the pattern
1252 * of application names and return codes we will look for. */
1254 /* Test unquoted existing filename with a space */
1255 {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}},
1256 {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}},
1258 /* Test quoted existing filename with a space */
1259 {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}},
1260 {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}},
1262 /* Test unquoted filename with a space that doesn't exist, but
1264 {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}},
1265 {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}},
1267 /* Test quoted filename with a space that does not exist */
1268 {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}},
1269 {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}},
1271 /* Test filename supplied without the extension */
1272 {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}},
1273 {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}},
1275 /* Test an unquoted nonexistent filename */
1276 {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}},
1277 {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}},
1279 /* Test an application that will be found on the path */
1280 {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}},
1281 {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}},
1283 /* Test an application that will not be found on the path */
1284 {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1285 {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1287 {NULL
, {NULL
}, 0, {0}}
1290 static void test_dde_default_app(void)
1292 char filename
[MAX_PATH
];
1294 dde_thread_info_t info
= { filename
, GetCurrentThreadId() };
1295 const dde_default_app_tests_t
* test
;
1302 rc
= DdeInitializeA(&ddeInst
, ddeCb
, CBF_SKIP_ALLNOTIFICATIONS
| CBF_FAIL_ADVISES
|
1303 CBF_FAIL_POKES
| CBF_FAIL_REQUESTS
, 0L);
1304 assert(rc
== DMLERR_NO_ERROR
);
1306 sprintf(filename
, "%s\\test file.sde", tmpdir
);
1308 /* It is strictly not necessary to register an application name here, but wine's
1309 * DdeNameService implementation complains if 0L is passed instead of
1310 * hszApplication with DNS_FILTEROFF */
1311 hszApplication
= DdeCreateStringHandleA(ddeInst
, "shlexec", CP_WINANSI
);
1312 hszTopic
= DdeCreateStringHandleA(ddeInst
, "shlexec", CP_WINANSI
);
1313 assert(hszApplication
&& hszTopic
);
1314 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_REGISTER
| DNS_FILTEROFF
));
1316 test
= dde_default_app_tests
;
1317 while (test
->command
)
1319 create_test_association(".sde");
1320 sprintf(params
, test
->command
, tmpdir
);
1321 create_test_verb_dde(".sde", "Open", 1, params
, "[test]", NULL
,
1323 denyNextConnection
= FALSE
;
1324 ddeApplication
[0] = 0;
1326 /* No application will be run as we will respond to the first DDE event,
1327 * so don't wait for it */
1330 assert(CreateThread(NULL
, 0, ddeThread
, (LPVOID
)&info
, 0, &threadId
));
1331 while (GetMessage(&msg
, NULL
, 0, 0)) DispatchMessage(&msg
);
1332 rc
= msg
.wParam
> 32 ? 33 : msg
.wParam
;
1334 /* First test, find which set of test data we expect to see */
1335 if (test
== dde_default_app_tests
)
1338 for (i
=0; i
<DDE_DEFAULT_APP_VARIANTS
; i
++)
1340 if (!strcmp(ddeApplication
, test
->expectedDdeApplication
[i
]))
1346 if (i
== DDE_DEFAULT_APP_VARIANTS
)
1347 skip("Default DDE application test does not match any available results, using first expected data set.\n");
1350 if ((test
->todo
& 0x1)==0)
1352 ok(rc
==test
->rc
[which
], "%s failed: rc=%d err=%d\n", shell_call
,
1353 rc
, GetLastError());
1357 ok(rc
==test
->rc
[which
], "%s failed: rc=%d err=%d\n", shell_call
,
1358 rc
, GetLastError());
1362 if ((test
->todo
& 0x2)==0)
1364 ok(!strcmp(ddeApplication
, test
->expectedDdeApplication
[which
]),
1365 "Expected application '%s', got '%s'\n",
1366 test
->expectedDdeApplication
[which
], ddeApplication
);
1370 ok(!strcmp(ddeApplication
, test
->expectedDdeApplication
[which
]),
1371 "Expected application '%s', got '%s'\n",
1372 test
->expectedDdeApplication
[which
], ddeApplication
);
1376 delete_test_association(".sde");
1380 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_UNREGISTER
));
1381 assert(DdeFreeStringHandle(ddeInst
, hszTopic
));
1382 assert(DdeFreeStringHandle(ddeInst
, hszApplication
));
1383 assert(DdeUninitialize(ddeInst
));
1386 static void init_test(void)
1389 HRESULT (WINAPI
*pDllGetVersion
)(DLLVERSIONINFO
*);
1390 char filename
[MAX_PATH
];
1391 WCHAR lnkfile
[MAX_PATH
];
1393 const char* const * testfile
;
1398 hdll
=GetModuleHandleA("shell32.dll");
1399 pDllGetVersion
=(void*)GetProcAddress(hdll
, "DllGetVersion");
1402 dllver
.cbSize
=sizeof(dllver
);
1403 pDllGetVersion(&dllver
);
1404 trace("major=%d minor=%d build=%d platform=%d\n",
1405 dllver
.dwMajorVersion
, dllver
.dwMinorVersion
,
1406 dllver
.dwBuildNumber
, dllver
.dwPlatformID
);
1410 memset(&dllver
, 0, sizeof(dllver
));
1413 r
= CoInitialize(NULL
);
1414 ok(SUCCEEDED(r
), "CoInitialize failed (0x%08x)\n", r
);
1418 rc
=GetModuleFileName(NULL
, argv0
, sizeof(argv0
));
1419 assert(rc
!=0 && rc
<sizeof(argv0
));
1420 if (GetFileAttributes(argv0
)==INVALID_FILE_ATTRIBUTES
)
1422 strcat(argv0
, ".so");
1423 ok(GetFileAttributes(argv0
)!=INVALID_FILE_ATTRIBUTES
,
1424 "unable to find argv0!\n");
1427 GetTempPathA(sizeof(tmpdir
)/sizeof(*tmpdir
), tmpdir
);
1428 assert(GetTempFileNameA(tmpdir
, "wt", 0, child_file
)!=0);
1429 init_event(child_file
);
1431 /* Set up the test files */
1437 sprintf(filename
, *testfile
, tmpdir
);
1438 hfile
=CreateFile(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
1439 FILE_ATTRIBUTE_NORMAL
, NULL
);
1440 if (hfile
==INVALID_HANDLE_VALUE
)
1442 trace("unable to create '%s': err=%d\n", filename
, GetLastError());
1449 /* Setup the test shortcuts */
1450 sprintf(filename
, "%s\\test_shortcut_shlexec.lnk", tmpdir
);
1451 MultiByteToWideChar(CP_ACP
, 0, filename
, -1, lnkfile
, sizeof(lnkfile
)/sizeof(*lnkfile
));
1452 desc
.description
=NULL
;
1454 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
1457 desc
.arguments
="ignored";
1462 create_lnk(lnkfile
, &desc
, 0);
1464 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
1465 MultiByteToWideChar(CP_ACP
, 0, filename
, -1, lnkfile
, sizeof(lnkfile
)/sizeof(*lnkfile
));
1466 desc
.description
=NULL
;
1470 sprintf(params
, "shlexec \"%s\" Lnk", child_file
);
1471 desc
.arguments
=params
;
1476 create_lnk(lnkfile
, &desc
, 0);
1478 /* Create a basic association suitable for most tests */
1479 create_test_association(".shlexec");
1480 create_test_verb(".shlexec", "Open", 0, "Open \"%1\"");
1481 create_test_verb(".shlexec", "NoQuotes", 0, "NoQuotes %1");
1482 create_test_verb(".shlexec", "LowerL", 0, "LowerL %l");
1483 create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
1484 create_test_verb(".shlexec", "UpperL", 0, "UpperL %L");
1485 create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
1488 static void cleanup_test(void)
1490 char filename
[MAX_PATH
];
1491 const char* const * testfile
;
1493 /* Delete the test files */
1497 sprintf(filename
, *testfile
, tmpdir
);
1498 DeleteFile(filename
);
1501 DeleteFile(child_file
);
1503 /* Delete the test association */
1504 delete_test_association(".shlexec");
1506 CloseHandle(hEvent
);
1514 myARGC
= winetest_get_mainargs(&myARGV
);
1517 doChild(myARGC
, myARGV
);
1524 test_find_executable();
1529 test_dde_default_app();