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
35 /* Needed to get SEE_MASK_NOZONECHECKS with the PSDK */
36 #define NTDDI_WINXPSP1 0x05010100
37 #define NTDDI_VERSION NTDDI_WINXPSP1
38 #define _WIN32_WINNT 0x0501
48 #include "wine/test.h"
50 #include "shell32_test.h"
53 static char argv0
[MAX_PATH
];
56 static char tmpdir
[MAX_PATH
];
57 static char child_file
[MAX_PATH
];
58 static DLLVERSIONINFO dllver
;
63 * ShellExecute wrappers
66 static void dump_child(void);
69 static void init_event(const char* child_file
)
72 event_name
=strrchr(child_file
, '\\')+1;
73 hEvent
=CreateEvent(NULL
, FALSE
, FALSE
, event_name
);
76 static void strcat_param(char* str
, const char* param
)
90 static char shell_call
[2048]="";
91 static int shell_execute(LPCSTR operation
, LPCSTR file
, LPCSTR parameters
, LPCSTR directory
)
95 strcpy(shell_call
, "ShellExecute(");
96 strcat_param(shell_call
, operation
);
97 strcat(shell_call
, ", ");
98 strcat_param(shell_call
, file
);
99 strcat(shell_call
, ", ");
100 strcat_param(shell_call
, parameters
);
101 strcat(shell_call
, ", ");
102 strcat_param(shell_call
, directory
);
103 strcat(shell_call
, ")");
104 if (winetest_debug
> 1)
105 trace("%s\n", shell_call
);
107 DeleteFile(child_file
);
108 SetLastError(0xcafebabe);
110 /* FIXME: We cannot use ShellExecuteEx() here because if there is no
111 * association it displays the 'Open With' dialog and I could not find
112 * a flag to prevent this.
114 rc
=(INT_PTR
)ShellExecute(NULL
, operation
, file
, parameters
, directory
, SW_SHOWNORMAL
);
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_PTR
)sei
.hInstApp
;
171 ok((success
&& rc
> 32) || (!success
&& rc
<= 32),
172 "%s rc=%d and hInstApp=%ld 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 BOOL
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 if (rc
!= ERROR_SUCCESS
)
215 rc
=RegSetValueEx(hkey
, NULL
, 0, REG_SZ
, (LPBYTE
) class, strlen(class)+1);
216 ok(rc
==ERROR_SUCCESS
, "RegSetValueEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc
);
219 rc
=RegCreateKeyEx(HKEY_CLASSES_ROOT
, class, 0, NULL
, 0,
220 KEY_CREATE_SUB_KEY
| KEY_ENUMERATE_SUB_KEYS
, NULL
, &hkey
, NULL
);
221 ok(rc
==ERROR_SUCCESS
, "RegCreateKeyEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc
);
223 rc
=RegCreateKeyEx(hkey
, "shell", 0, NULL
, 0,
224 KEY_CREATE_SUB_KEY
, NULL
, &hkey_shell
, NULL
);
225 ok(rc
==ERROR_SUCCESS
, "RegCreateKeyEx 'shell' failed, expected ERROR_SUCCESS, got %d\n", rc
);
228 CloseHandle(hkey_shell
);
233 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
234 static LSTATUS
myRegDeleteTreeA(HKEY hKey
, LPCSTR lpszSubKey
)
237 DWORD dwMaxSubkeyLen
, dwMaxValueLen
;
238 DWORD dwMaxLen
, dwSize
;
239 CHAR szNameBuf
[MAX_PATH
], *lpszName
= szNameBuf
;
244 ret
= RegOpenKeyExA(hKey
, lpszSubKey
, 0, KEY_READ
, &hSubKey
);
248 /* Get highest length for keys, values */
249 ret
= RegQueryInfoKeyA(hSubKey
, NULL
, NULL
, NULL
, NULL
,
250 &dwMaxSubkeyLen
, NULL
, NULL
, &dwMaxValueLen
, NULL
, NULL
, NULL
);
251 if (ret
) goto cleanup
;
255 dwMaxLen
= max(dwMaxSubkeyLen
, dwMaxValueLen
);
256 if (dwMaxLen
> sizeof(szNameBuf
)/sizeof(CHAR
))
258 /* Name too big: alloc a buffer for it */
259 if (!(lpszName
= HeapAlloc( GetProcessHeap(), 0, dwMaxLen
*sizeof(CHAR
))))
261 ret
= ERROR_NOT_ENOUGH_MEMORY
;
267 /* Recursively delete all the subkeys */
271 if (RegEnumKeyExA(hSubKey
, 0, lpszName
, &dwSize
, NULL
,
272 NULL
, NULL
, NULL
)) break;
274 ret
= myRegDeleteTreeA(hSubKey
, lpszName
);
275 if (ret
) goto cleanup
;
279 ret
= RegDeleteKeyA(hKey
, lpszSubKey
);
284 if (RegEnumValueA(hKey
, 0, lpszName
, &dwSize
,
285 NULL
, NULL
, NULL
, NULL
)) break;
287 ret
= RegDeleteValueA(hKey
, lpszName
);
288 if (ret
) goto cleanup
;
292 /* Free buffer if allocated */
293 if (lpszName
!= szNameBuf
)
294 HeapFree( GetProcessHeap(), 0, lpszName
);
296 RegCloseKey(hSubKey
);
300 static void delete_test_association(const char* extension
)
302 char class[MAX_PATH
];
304 sprintf(class, "shlexec%s", extension
);
305 myRegDeleteTreeA(HKEY_CLASSES_ROOT
, class);
306 myRegDeleteTreeA(HKEY_CLASSES_ROOT
, extension
);
309 static void create_test_verb_dde(const char* extension
, const char* verb
,
310 int rawcmd
, const char* cmdtail
, const char *ddeexec
,
311 const char *application
, const char *topic
,
314 HKEY hkey_shell
, hkey_verb
, hkey_cmd
;
315 char shell
[MAX_PATH
];
319 sprintf(shell
, "shlexec%s\\shell", extension
);
320 rc
=RegOpenKeyEx(HKEY_CLASSES_ROOT
, shell
, 0,
321 KEY_CREATE_SUB_KEY
, &hkey_shell
);
322 assert(rc
==ERROR_SUCCESS
);
323 rc
=RegCreateKeyEx(hkey_shell
, verb
, 0, NULL
, 0, KEY_CREATE_SUB_KEY
,
324 NULL
, &hkey_verb
, NULL
);
325 assert(rc
==ERROR_SUCCESS
);
326 rc
=RegCreateKeyEx(hkey_verb
, "command", 0, NULL
, 0, KEY_SET_VALUE
,
327 NULL
, &hkey_cmd
, NULL
);
328 assert(rc
==ERROR_SUCCESS
);
332 rc
=RegSetValueEx(hkey_cmd
, NULL
, 0, REG_SZ
, (LPBYTE
)cmdtail
, strlen(cmdtail
)+1);
336 cmd
=HeapAlloc(GetProcessHeap(), 0, strlen(argv0
)+10+strlen(child_file
)+2+strlen(cmdtail
)+1);
337 sprintf(cmd
,"%s shlexec \"%s\" %s", argv0
, child_file
, cmdtail
);
338 rc
=RegSetValueEx(hkey_cmd
, NULL
, 0, REG_SZ
, (LPBYTE
)cmd
, strlen(cmd
)+1);
339 assert(rc
==ERROR_SUCCESS
);
340 HeapFree(GetProcessHeap(), 0, cmd
);
345 HKEY hkey_ddeexec
, hkey_application
, hkey_topic
, hkey_ifexec
;
347 rc
=RegCreateKeyEx(hkey_verb
, "ddeexec", 0, NULL
, 0, KEY_SET_VALUE
|
348 KEY_CREATE_SUB_KEY
, NULL
, &hkey_ddeexec
, NULL
);
349 assert(rc
==ERROR_SUCCESS
);
350 rc
=RegSetValueEx(hkey_ddeexec
, NULL
, 0, REG_SZ
, (LPBYTE
)ddeexec
,
352 assert(rc
==ERROR_SUCCESS
);
355 rc
=RegCreateKeyEx(hkey_ddeexec
, "application", 0, NULL
, 0, KEY_SET_VALUE
,
356 NULL
, &hkey_application
, NULL
);
357 assert(rc
==ERROR_SUCCESS
);
358 rc
=RegSetValueEx(hkey_application
, NULL
, 0, REG_SZ
, (LPBYTE
)application
,
359 strlen(application
)+1);
360 assert(rc
==ERROR_SUCCESS
);
361 CloseHandle(hkey_application
);
365 rc
=RegCreateKeyEx(hkey_ddeexec
, "topic", 0, NULL
, 0, KEY_SET_VALUE
,
366 NULL
, &hkey_topic
, NULL
);
367 assert(rc
==ERROR_SUCCESS
);
368 rc
=RegSetValueEx(hkey_topic
, NULL
, 0, REG_SZ
, (LPBYTE
)topic
,
370 assert(rc
==ERROR_SUCCESS
);
371 CloseHandle(hkey_topic
);
375 rc
=RegCreateKeyEx(hkey_ddeexec
, "ifexec", 0, NULL
, 0, KEY_SET_VALUE
,
376 NULL
, &hkey_ifexec
, NULL
);
377 assert(rc
==ERROR_SUCCESS
);
378 rc
=RegSetValueEx(hkey_ifexec
, NULL
, 0, REG_SZ
, (LPBYTE
)ifexec
,
380 assert(rc
==ERROR_SUCCESS
);
381 CloseHandle(hkey_ifexec
);
383 CloseHandle(hkey_ddeexec
);
386 CloseHandle(hkey_shell
);
387 CloseHandle(hkey_verb
);
388 CloseHandle(hkey_cmd
);
391 static void create_test_verb(const char* extension
, const char* verb
,
392 int rawcmd
, const char* cmdtail
)
394 create_test_verb_dde(extension
, verb
, rawcmd
, cmdtail
, NULL
, NULL
,
400 * Functions to check that the child process was started just right
401 * (borrowed from dlls/kernel32/tests/process.c)
405 static const char* encodeA(const char* str
)
407 static char encoded
[2*1024+1];
412 len
= strlen(str
) + 1;
413 if (len
>= sizeof(encoded
)/2)
415 fprintf(stderr
, "string is too long!\n");
419 for (i
= 0; i
< len
; i
++)
420 sprintf(&ptr
[i
* 2], "%02x", (unsigned char)str
[i
]);
425 static unsigned decode_char(char c
)
427 if (c
>= '0' && c
<= '9') return c
- '0';
428 if (c
>= 'a' && c
<= 'f') return c
- 'a' + 10;
429 assert(c
>= 'A' && c
<= 'F');
433 static char* decodeA(const char* str
)
435 static char decoded
[1024];
439 len
= strlen(str
) / 2;
440 if (!len
--) return NULL
;
441 if (len
>= sizeof(decoded
))
443 fprintf(stderr
, "string is too long!\n");
447 for (i
= 0; i
< len
; i
++)
448 ptr
[i
] = (decode_char(str
[2 * i
]) << 4) | decode_char(str
[2 * i
+ 1]);
453 static void childPrintf(HANDLE h
, const char* fmt
, ...)
459 va_start(valist
, fmt
);
460 vsprintf(buffer
, fmt
, valist
);
462 WriteFile(h
, buffer
, strlen(buffer
), &w
, NULL
);
465 static void doChild(int argc
, char** argv
)
472 hFile
=CreateFileA(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, 0);
473 if (hFile
== INVALID_HANDLE_VALUE
)
477 childPrintf(hFile
, "[Arguments]\r\n");
478 if (winetest_debug
> 2)
479 trace("argcA=%d\n", argc
);
480 childPrintf(hFile
, "argcA=%d\r\n", argc
);
481 for (i
= 0; i
< argc
; i
++)
483 if (winetest_debug
> 2)
484 trace("argvA%d=%s\n", i
, argv
[i
]);
485 childPrintf(hFile
, "argvA%d=%s\r\n", i
, encodeA(argv
[i
]));
489 init_event(filename
);
494 static char* getChildString(const char* sect
, const char* key
)
499 GetPrivateProfileStringA(sect
, key
, "-", buf
, sizeof(buf
), child_file
);
500 if (buf
[0] == '\0' || (buf
[0] == '-' && buf
[1] == '\0')) return NULL
;
501 assert(!(strlen(buf
) & 1));
506 static void dump_child(void)
508 if (winetest_debug
> 1)
514 c
=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file
);
515 trace("argcA=%d\n",c
);
518 sprintf(key
, "argvA%d", i
);
519 str
=getChildString("Arguments", key
);
520 trace("%s=%s\n", key
, str
);
525 static int StrCmpPath(const char* s1
, const char* s2
)
527 if (!s1
&& !s2
) return 0;
538 if ((*s1
=='/' || *s1
=='\\') && (*s2
=='/' || *s2
=='\\'))
540 while (*s1
=='/' || *s1
=='\\')
542 while (*s2
=='/' || *s2
=='\\')
545 else if (toupper(*s1
)==toupper(*s2
))
562 static int _okChildString(const char* file
, int line
, const char* key
, const char* expected
)
565 result
=getChildString("Arguments", key
);
566 return ok_(file
, line
)(lstrcmpiA(result
, expected
) == 0,
567 "%s expected '%s', got '%s'\n", key
, expected
, result
);
570 static int _okChildPath(const char* file
, int line
, const char* key
, const char* expected
)
573 result
=getChildString("Arguments", key
);
574 return ok_(file
, line
)(StrCmpPath(result
, expected
) == 0,
575 "%s expected '%s', got '%s'\n", key
, expected
, result
);
578 static int _okChildInt(const char* file
, int line
, const char* key
, int expected
)
581 result
=GetPrivateProfileIntA("Arguments", key
, expected
, child_file
);
582 return ok_(file
, line
)(result
== expected
,
583 "%s expected %d, but got %d\n", key
, expected
, result
);
586 #define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
587 #define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
588 #define okChildInt(key, expected) _okChildInt(__FILE__, __LINE__, (key), (expected))
598 static const char* testfiles
[]=
600 "%s\\test file.shlexec",
601 "%s\\%%nasty%% $file.shlexec",
602 "%s\\test file.noassoc",
603 "%s\\test file.noassoc.shlexec",
604 "%s\\test file.shlexec.noassoc",
605 "%s\\test_shortcut_shlexec.lnk",
606 "%s\\test_shortcut_exe.lnk",
608 "%s\\test file.shlfoo",
610 "%s\\masked file.shlexec",
621 const char* basename
;
626 static filename_tests_t filename_tests
[]=
628 /* Test bad / nonexistent filenames */
629 {NULL
, "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF
},
630 {NULL
, "%s\\nonexistent.noassoc", 0x0, SE_ERR_FNF
},
633 {NULL
, "%s\\test file.shlexec", 0x0, 33},
634 {NULL
, "%s\\test file.shlexec.", 0x0, 33},
635 {NULL
, "%s\\%%nasty%% $file.shlexec", 0x0, 33},
636 {NULL
, "%s/test file.shlexec", 0x0, 33},
638 /* Test filenames with no association */
639 {NULL
, "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC
},
641 /* Test double extensions */
642 {NULL
, "%s\\test file.noassoc.shlexec", 0x0, 33},
643 {NULL
, "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC
},
645 /* Test alternate verbs */
646 {"LowerL", "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF
},
647 {"LowerL", "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC
},
649 {"QuotedLowerL", "%s\\test file.shlexec", 0x0, 33},
650 {"QuotedUpperL", "%s\\test file.shlexec", 0x0, 33},
652 /* Test file masked due to space */
653 {NULL
, "%s\\masked file.shlexec", 0x1, 33},
654 /* Test if quoting prevents the masking */
655 {NULL
, "%s\\masked file.shlexec", 0x40, 33},
660 static filename_tests_t noquotes_tests
[]=
662 /* Test unquoted '%1' thingies */
663 {"NoQuotes", "%s\\test file.shlexec", 0xa, 33},
664 {"LowerL", "%s\\test file.shlexec", 0xa, 33},
665 {"UpperL", "%s\\test file.shlexec", 0xa, 33},
670 static void test_filename(void)
672 char filename
[MAX_PATH
];
673 const filename_tests_t
* test
;
678 while (test
->basename
)
680 BOOL quotedfile
= FALSE
;
682 sprintf(filename
, test
->basename
, tmpdir
);
683 if (strchr(filename
, '/'))
693 if ((test
->todo
& 0x40)==0)
695 rc
=shell_execute(test
->verb
, filename
, NULL
, NULL
);
699 char quoted
[MAX_PATH
+ 2];
702 sprintf(quoted
, "\"%s\"", filename
);
703 rc
=shell_execute(test
->verb
, quoted
, NULL
, NULL
);
707 if ((test
->todo
& 0x1)==0)
710 broken(quotedfile
&& rc
== 2), /* NT4 */
711 "%s failed: rc=%d err=%d\n", shell_call
,
716 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
722 if ((test
->todo
& 0x2)==0)
724 okChildInt("argcA", 5);
728 okChildInt("argcA", 5);
730 verb
=(test
->verb
? test
->verb
: "Open");
731 if ((test
->todo
& 0x4)==0)
733 okChildString("argvA3", verb
);
737 okChildString("argvA3", verb
);
739 if ((test
->todo
& 0x8)==0)
741 okChildPath("argvA4", filename
);
745 okChildPath("argvA4", filename
);
752 while (test
->basename
)
754 sprintf(filename
, test
->basename
, tmpdir
);
755 rc
=shell_execute(test
->verb
, filename
, NULL
, NULL
);
758 if ((test
->todo
& 0x1)==0)
760 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
765 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
774 verb
=(test
->verb
? test
->verb
: "Open");
775 if ((test
->todo
& 0x4)==0)
777 okChildString("argvA3", verb
);
781 okChildString("argvA3", verb
);
790 space
=strchr(str
, ' ');
793 sprintf(attrib
, "argvA%d", count
);
794 if ((test
->todo
& 0x8)==0)
796 okChildPath(attrib
, str
);
800 okChildPath(attrib
, str
);
807 if ((test
->todo
& 0x2)==0)
809 okChildInt("argcA", count
);
813 okChildInt("argcA", count
);
819 if (dllver
.dwMajorVersion
!= 0)
821 /* The more recent versions of shell32.dll accept quoted filenames
822 * while older ones (e.g. 4.00) don't. Still we want to test this
823 * because IE 6 depends on the new behavior.
824 * One day we may need to check the exact version of the dll but for
825 * now making sure DllGetVersion() is present is sufficient.
827 sprintf(filename
, "\"%s\\test file.shlexec\"", tmpdir
);
828 rc
=shell_execute(NULL
, filename
, NULL
, NULL
);
829 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
831 okChildInt("argcA", 5);
832 okChildString("argvA3", "Open");
833 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
834 okChildPath("argvA4", filename
);
838 static void test_find_executable(void)
840 char filename
[MAX_PATH
];
841 char command
[MAX_PATH
];
842 const filename_tests_t
* test
;
845 if (!create_test_association(".sfe"))
847 skip("Unable to create association for '.sfe'\n");
850 create_test_verb(".sfe", "Open", 1, "%1");
852 /* Don't test FindExecutable(..., NULL), it always crashes */
854 strcpy(command
, "your word");
855 if (0) /* Can crash on Vista! */
857 rc
=(INT_PTR
)FindExecutableA(NULL
, NULL
, command
);
858 ok(rc
== SE_ERR_FNF
|| rc
> 32 /* nt4 */, "FindExecutable(NULL) returned %ld\n", rc
);
859 ok(strcmp(command
, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command
);
862 strcpy(command
, "your word");
863 rc
=(INT_PTR
)FindExecutableA(tmpdir
, NULL
, command
);
864 ok(rc
== SE_ERR_NOASSOC
/* >= win2000 */ || rc
> 32 /* win98, nt4 */, "FindExecutable(NULL) returned %ld\n", rc
);
865 ok(strcmp(command
, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command
);
867 /* Win95 can't cope with double backslashes in FindExecutableA (tmpdir has a trailing backslash) */
868 sprintf(filename
, "%stest file.sfe", tmpdir
);
869 rc
=(INT_PTR
)FindExecutableA(filename
, NULL
, command
);
870 ok(rc
> 32, "FindExecutable(%s) returned %ld\n", filename
, rc
);
871 /* Depending on the platform, command could be '%1' or 'test file.sfe' */
873 rc
=(INT_PTR
)FindExecutableA("test file.sfe", tmpdir
, command
);
874 ok(rc
> 32, "FindExecutable(%s) returned %ld\n", filename
, rc
);
876 rc
=(INT_PTR
)FindExecutableA("test file.sfe", NULL
, command
);
877 ok(rc
== SE_ERR_FNF
, "FindExecutable(%s) returned %ld\n", filename
, rc
);
879 delete_test_association(".sfe");
881 if (!create_test_association(".shl"))
883 skip("Unable to create association for '.shl'\n");
886 create_test_verb(".shl", "Open", 0, "Open");
888 sprintf(filename
, "%s\\test file.shl", tmpdir
);
889 rc
=(INT_PTR
)FindExecutableA(filename
, NULL
, command
);
890 ok(rc
== SE_ERR_FNF
/* NT4 */ || rc
> 32, "FindExecutable(%s) returned %ld\n", filename
, rc
);
892 sprintf(filename
, "%s\\test file.shlfoo", tmpdir
);
893 rc
=(INT_PTR
)FindExecutableA(filename
, NULL
, command
);
895 delete_test_association(".shl");
899 /* On Windows XP and 2003 FindExecutable() is completely broken.
900 * Probably what it does is convert the filename to 8.3 format,
901 * which as a side effect converts the '.shlfoo' extension to '.shl',
902 * and then tries to find an association for '.shl'. This means it
903 * will normally fail on most extensions with more than 3 characters,
905 * Also it means we cannot do any other test.
907 trace("FindExecutable() is broken -> skipping 4+ character extension tests\n");
912 while (test
->basename
)
914 /* Win95 can't cope with double slashes/backslashes in FindExecutableA */
915 if (tmpdir
[strlen(tmpdir
) - 1] == '\\')
916 tmpdir
[strlen(tmpdir
) - 1] = 0;
918 sprintf(filename
, test
->basename
, tmpdir
);
919 if (strchr(filename
, '/'))
930 /* Win98 does not '\0'-terminate command! */
931 memset(command
, '\0', sizeof(command
));
932 rc
=(INT_PTR
)FindExecutableA(filename
, NULL
, command
);
935 if ((test
->todo
& 0x10)==0)
937 ok(rc
==test
->rc
, "FindExecutable(%s) failed: rc=%ld\n", filename
, rc
);
941 ok(rc
==test
->rc
, "FindExecutable(%s) failed: rc=%ld\n", filename
, rc
);
946 equal
=strcmp(command
, argv0
) == 0 ||
947 /* NT4 returns an extra 0x8 character! */
948 (strlen(command
) == strlen(argv0
)+1 && strncmp(command
, argv0
, strlen(argv0
)) == 0);
949 if ((test
->todo
& 0x20)==0)
951 ok(equal
, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
952 filename
, command
, argv0
);
956 ok(equal
, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
957 filename
, command
, argv0
);
965 static filename_tests_t lnk_tests
[]=
967 /* Pass bad / nonexistent filenames as a parameter */
968 {NULL
, "%s\\nonexistent.shlexec", 0xa, 33},
969 {NULL
, "%s\\nonexistent.noassoc", 0xa, 33},
971 /* Pass regular paths as a parameter */
972 {NULL
, "%s\\test file.shlexec", 0xa, 33},
973 {NULL
, "%s/%%nasty%% $file.shlexec", 0xa, 33},
975 /* Pass filenames with no association as a parameter */
976 {NULL
, "%s\\test file.noassoc", 0xa, 33},
981 static void test_lnks(void)
983 char filename
[MAX_PATH
];
984 char params
[MAX_PATH
];
985 const filename_tests_t
* test
;
988 sprintf(filename
, "%s\\test_shortcut_shlexec.lnk", tmpdir
);
989 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
990 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
992 okChildInt("argcA", 5);
993 okChildString("argvA3", "Open");
994 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
995 okChildPath("argvA4", filename
);
997 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
998 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
999 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
1001 okChildInt("argcA", 4);
1002 okChildString("argvA3", "Lnk");
1004 if (dllver
.dwMajorVersion
>=6)
1007 /* Recent versions of shell32.dll accept '/'s in shortcut paths.
1008 * Older versions don't or are quite buggy in this regard.
1010 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
1018 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, NULL
, NULL
);
1019 ok(rc
> 32, "%s failed: rc=%d err=%d\n", shell_call
, rc
,
1021 okChildInt("argcA", 4);
1022 okChildString("argvA3", "Lnk");
1025 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
1027 while (test
->basename
)
1030 sprintf(params
+1, test
->basename
, tmpdir
);
1031 strcat(params
,"\"");
1032 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, filename
, params
,
1036 if ((test
->todo
& 0x1)==0)
1038 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1039 rc
, GetLastError());
1043 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1044 rc
, GetLastError());
1048 if ((test
->todo
& 0x2)==0)
1050 okChildInt("argcA", 5);
1054 okChildInt("argcA", 5);
1056 if ((test
->todo
& 0x4)==0)
1058 okChildString("argvA3", "Lnk");
1062 okChildString("argvA3", "Lnk");
1064 sprintf(params
, test
->basename
, tmpdir
);
1065 if ((test
->todo
& 0x8)==0)
1067 okChildPath("argvA4", params
);
1071 okChildPath("argvA4", params
);
1079 static void test_exes(void)
1081 char filename
[MAX_PATH
];
1085 sprintf(params
, "shlexec \"%s\" Exec", child_file
);
1087 /* We need NOZONECHECKS on Win2003 to block a dialog */
1088 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, argv0
, params
,
1090 ok(rc
> 32, "%s returned %d\n", shell_call
, rc
);
1091 okChildInt("argcA", 4);
1092 okChildString("argvA3", "Exec");
1094 sprintf(filename
, "%s\\test file.noassoc", tmpdir
);
1095 if (CopyFile(argv0
, filename
, FALSE
))
1097 rc
=shell_execute(NULL
, filename
, params
, NULL
);
1099 ok(rc
==SE_ERR_NOASSOC
, "%s succeeded: rc=%d\n", shell_call
, rc
);
1104 static void test_exes_long(void)
1106 char filename
[MAX_PATH
];
1108 char longparam
[MAX_PATH
];
1111 for (rc
= 0; rc
< MAX_PATH
; rc
++)
1112 longparam
[rc
]='a'+rc
%26;
1113 longparam
[MAX_PATH
-1]=0;
1116 sprintf(params
, "shlexec \"%s\" %s", child_file
,longparam
);
1118 /* We need NOZONECHECKS on Win2003 to block a dialog */
1119 rc
=shell_execute_ex(SEE_MASK_NOZONECHECKS
, NULL
, argv0
, params
,
1121 ok(rc
> 32, "%s returned %d\n", shell_call
, rc
);
1122 okChildInt("argcA", 4);
1123 okChildString("argvA3", longparam
);
1125 sprintf(filename
, "%s\\test file.noassoc", tmpdir
);
1126 if (CopyFile(argv0
, filename
, FALSE
))
1128 rc
=shell_execute(NULL
, filename
, params
, NULL
);
1130 ok(rc
==SE_ERR_NOASSOC
, "%s succeeded: rc=%d\n", shell_call
, rc
);
1137 const char* command
;
1138 const char* ddeexec
;
1139 const char* application
;
1143 const char* expectedDdeExec
;
1148 static dde_tests_t dde_tests
[] =
1150 /* Test passing and not passing command-line
1151 * argument, no DDE */
1152 {"", NULL
, NULL
, NULL
, NULL
, FALSE
, "", 0x0, 33},
1153 {"\"%1\"", NULL
, NULL
, NULL
, NULL
, TRUE
, "", 0x0, 33},
1155 /* Test passing and not passing command-line
1156 * argument, with DDE */
1157 {"", "[open(\"%1\")]", "shlexec", "dde", NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1158 {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL
, TRUE
, "[open(\"%s\")]", 0x0, 33},
1160 /* Test unquoted %1 in command and ddeexec
1161 * (test filename has space) */
1162 {"%1", "[open(%1)]", "shlexec", "dde", NULL
, 2, "[open(%s)]", 0x0, 33},
1164 /* Test ifexec precedence over ddeexec */
1165 {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE
, "[ifexec(\"%s\")]", 0x0, 33},
1167 /* Test default DDE topic */
1168 {"", "[open(\"%1\")]", "shlexec", NULL
, NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1170 /* Test default DDE application */
1171 {"", "[open(\"%1\")]", NULL
, "dde", NULL
, FALSE
, "[open(\"%s\")]", 0x0, 33},
1173 {NULL
, NULL
, NULL
, NULL
, NULL
, 0, 0x0, 0}
1176 static DWORD ddeInst
;
1177 static HSZ hszTopic
;
1178 static char ddeExec
[MAX_PATH
], ddeApplication
[MAX_PATH
];
1179 static BOOL denyNextConnection
;
1181 static HDDEDATA CALLBACK
ddeCb(UINT uType
, UINT uFmt
, HCONV hConv
,
1182 HSZ hsz1
, HSZ hsz2
, HDDEDATA hData
,
1183 ULONG_PTR dwData1
, ULONG_PTR dwData2
)
1187 if (winetest_debug
> 2)
1188 trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
1189 uType
, uFmt
, hConv
, hsz1
, hsz2
, hData
, dwData1
, dwData2
);
1194 if (!DdeCmpStringHandles(hsz1
, hszTopic
))
1196 if (denyNextConnection
)
1197 denyNextConnection
= FALSE
;
1200 size
= DdeQueryString(ddeInst
, hsz2
, ddeApplication
, MAX_PATH
, CP_WINANSI
);
1201 assert(size
< MAX_PATH
);
1202 return (HDDEDATA
)TRUE
;
1205 return (HDDEDATA
)FALSE
;
1208 size
= DdeGetData(hData
, (LPBYTE
)ddeExec
, MAX_PATH
, 0L);
1209 assert(size
< MAX_PATH
);
1210 DdeFreeDataHandle(hData
);
1211 return (HDDEDATA
)DDE_FACK
;
1221 DWORD threadIdParent
;
1222 } dde_thread_info_t
;
1224 static DWORD CALLBACK
ddeThread(LPVOID arg
)
1226 dde_thread_info_t
*info
= arg
;
1227 assert(info
&& info
->filename
);
1228 PostThreadMessage(info
->threadIdParent
,
1230 shell_execute_ex(SEE_MASK_FLAG_DDEWAIT
| SEE_MASK_FLAG_NO_UI
, NULL
, info
->filename
, NULL
, NULL
),
1235 /* ShellExecute won't successfully send DDE commands to console applications after starting them,
1236 * so we run a DDE server in this application, deny the first connection request to make
1237 * ShellExecute start the application, and then process the next DDE connection in this application
1238 * to see the execute command that is sent. */
1239 static void test_dde(void)
1241 char filename
[MAX_PATH
], defApplication
[MAX_PATH
];
1243 dde_thread_info_t info
= { filename
, GetCurrentThreadId() };
1244 const dde_tests_t
* test
;
1251 rc
= DdeInitializeA(&ddeInst
, ddeCb
, CBF_SKIP_ALLNOTIFICATIONS
| CBF_FAIL_ADVISES
|
1252 CBF_FAIL_POKES
| CBF_FAIL_REQUESTS
, 0L);
1253 assert(rc
== DMLERR_NO_ERROR
);
1255 sprintf(filename
, "%s\\test file.sde", tmpdir
);
1257 /* Default service is application name minus path and extension */
1258 strcpy(defApplication
, strrchr(argv0
, '\\')+1);
1259 *strchr(defApplication
, '.') = 0;
1262 while (test
->command
)
1264 if (!create_test_association(".sde"))
1266 skip("Unable to create association for '.sfe'\n");
1269 create_test_verb_dde(".sde", "Open", 0, test
->command
, test
->ddeexec
,
1270 test
->application
, test
->topic
, test
->ifexec
);
1271 hszApplication
= DdeCreateStringHandleA(ddeInst
, test
->application
?
1272 test
->application
: defApplication
, CP_WINANSI
);
1273 hszTopic
= DdeCreateStringHandleA(ddeInst
, test
->topic
? test
->topic
: SZDDESYS_TOPIC
,
1275 assert(hszApplication
&& hszTopic
);
1276 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_REGISTER
));
1277 denyNextConnection
= TRUE
;
1280 assert(CreateThread(NULL
, 0, ddeThread
, &info
, 0, &threadId
));
1281 while (GetMessage(&msg
, NULL
, 0, 0)) DispatchMessage(&msg
);
1282 rc
= msg
.wParam
> 32 ? 33 : msg
.wParam
;
1283 if ((test
->todo
& 0x1)==0)
1285 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1286 rc
, GetLastError());
1290 ok(rc
==test
->rc
, "%s failed: rc=%d err=%d\n", shell_call
,
1291 rc
, GetLastError());
1295 if ((test
->todo
& 0x2)==0)
1297 okChildInt("argcA", test
->expectedArgs
+ 3);
1301 okChildInt("argcA", test
->expectedArgs
+ 3);
1303 if (test
->expectedArgs
== 1)
1305 if ((test
->todo
& 0x4) == 0)
1307 okChildPath("argvA3", filename
);
1311 okChildPath("argvA3", filename
);
1314 if ((test
->todo
& 0x8) == 0)
1316 sprintf(params
, test
->expectedDdeExec
, filename
);
1317 ok(StrCmpPath(params
, ddeExec
) == 0,
1318 "ddeexec expected '%s', got '%s'\n", params
, ddeExec
);
1322 sprintf(params
, test
->expectedDdeExec
, filename
);
1323 ok(StrCmpPath(params
, ddeExec
) == 0,
1324 "ddeexec expected '%s', got '%s'\n", params
, ddeExec
);
1328 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_UNREGISTER
));
1329 assert(DdeFreeStringHandle(ddeInst
, hszTopic
));
1330 assert(DdeFreeStringHandle(ddeInst
, hszApplication
));
1331 delete_test_association(".sde");
1335 assert(DdeUninitialize(ddeInst
));
1338 #define DDE_DEFAULT_APP_VARIANTS 2
1341 const char* command
;
1342 const char* expectedDdeApplication
[DDE_DEFAULT_APP_VARIANTS
];
1344 int rc
[DDE_DEFAULT_APP_VARIANTS
];
1345 } dde_default_app_tests_t
;
1347 static dde_default_app_tests_t dde_default_app_tests
[] =
1349 /* Windows XP and 98 handle default DDE app names in different ways.
1350 * The application name we see in the first test determines the pattern
1351 * of application names and return codes we will look for. */
1353 /* Test unquoted existing filename with a space */
1354 {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}},
1355 {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}},
1357 /* Test quoted existing filename with a space */
1358 {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}},
1359 {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}},
1361 /* Test unquoted filename with a space that doesn't exist, but
1363 {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}},
1364 {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}},
1366 /* Test quoted filename with a space that does not exist */
1367 {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}},
1368 {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}},
1370 /* Test filename supplied without the extension */
1371 {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}},
1372 {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}},
1374 /* Test an unquoted nonexistent filename */
1375 {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}},
1376 {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}},
1378 /* Test an application that will be found on the path */
1379 {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}},
1380 {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}},
1382 /* Test an application that will not be found on the path */
1383 {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1384 {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1386 {NULL
, {NULL
}, 0, {0}}
1389 static void test_dde_default_app(void)
1391 char filename
[MAX_PATH
];
1393 dde_thread_info_t info
= { filename
, GetCurrentThreadId() };
1394 const dde_default_app_tests_t
* test
;
1401 rc
= DdeInitializeA(&ddeInst
, ddeCb
, CBF_SKIP_ALLNOTIFICATIONS
| CBF_FAIL_ADVISES
|
1402 CBF_FAIL_POKES
| CBF_FAIL_REQUESTS
, 0L);
1403 assert(rc
== DMLERR_NO_ERROR
);
1405 sprintf(filename
, "%s\\test file.sde", tmpdir
);
1407 /* It is strictly not necessary to register an application name here, but wine's
1408 * DdeNameService implementation complains if 0L is passed instead of
1409 * hszApplication with DNS_FILTEROFF */
1410 hszApplication
= DdeCreateStringHandleA(ddeInst
, "shlexec", CP_WINANSI
);
1411 hszTopic
= DdeCreateStringHandleA(ddeInst
, "shlexec", CP_WINANSI
);
1412 assert(hszApplication
&& hszTopic
);
1413 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_REGISTER
| DNS_FILTEROFF
));
1415 test
= dde_default_app_tests
;
1416 while (test
->command
)
1418 if (!create_test_association(".sde"))
1420 skip("Unable to create association for '.sde'\n");
1423 sprintf(params
, test
->command
, tmpdir
);
1424 create_test_verb_dde(".sde", "Open", 1, params
, "[test]", NULL
,
1426 denyNextConnection
= FALSE
;
1427 ddeApplication
[0] = 0;
1429 /* No application will be run as we will respond to the first DDE event,
1430 * so don't wait for it */
1433 assert(CreateThread(NULL
, 0, ddeThread
, &info
, 0, &threadId
));
1434 while (GetMessage(&msg
, NULL
, 0, 0)) DispatchMessage(&msg
);
1435 rc
= msg
.wParam
> 32 ? 33 : msg
.wParam
;
1437 /* First test, find which set of test data we expect to see */
1438 if (test
== dde_default_app_tests
)
1441 for (i
=0; i
<DDE_DEFAULT_APP_VARIANTS
; i
++)
1443 if (!strcmp(ddeApplication
, test
->expectedDdeApplication
[i
]))
1449 if (i
== DDE_DEFAULT_APP_VARIANTS
)
1450 skip("Default DDE application test does not match any available results, using first expected data set.\n");
1453 if ((test
->todo
& 0x1)==0)
1455 ok(rc
==test
->rc
[which
], "%s failed: rc=%d err=%d\n", shell_call
,
1456 rc
, GetLastError());
1460 ok(rc
==test
->rc
[which
], "%s failed: rc=%d err=%d\n", shell_call
,
1461 rc
, GetLastError());
1465 if ((test
->todo
& 0x2)==0)
1467 ok(!strcmp(ddeApplication
, test
->expectedDdeApplication
[which
]),
1468 "Expected application '%s', got '%s'\n",
1469 test
->expectedDdeApplication
[which
], ddeApplication
);
1473 ok(!strcmp(ddeApplication
, test
->expectedDdeApplication
[which
]),
1474 "Expected application '%s', got '%s'\n",
1475 test
->expectedDdeApplication
[which
], ddeApplication
);
1479 delete_test_association(".sde");
1483 assert(DdeNameService(ddeInst
, hszApplication
, 0L, DNS_UNREGISTER
));
1484 assert(DdeFreeStringHandle(ddeInst
, hszTopic
));
1485 assert(DdeFreeStringHandle(ddeInst
, hszApplication
));
1486 assert(DdeUninitialize(ddeInst
));
1489 static void init_test(void)
1492 HRESULT (WINAPI
*pDllGetVersion
)(DLLVERSIONINFO
*);
1493 char filename
[MAX_PATH
];
1494 WCHAR lnkfile
[MAX_PATH
];
1496 const char* const * testfile
;
1501 hdll
=GetModuleHandleA("shell32.dll");
1502 pDllGetVersion
=(void*)GetProcAddress(hdll
, "DllGetVersion");
1505 dllver
.cbSize
=sizeof(dllver
);
1506 pDllGetVersion(&dllver
);
1507 trace("major=%d minor=%d build=%d platform=%d\n",
1508 dllver
.dwMajorVersion
, dllver
.dwMinorVersion
,
1509 dllver
.dwBuildNumber
, dllver
.dwPlatformID
);
1513 memset(&dllver
, 0, sizeof(dllver
));
1516 r
= CoInitialize(NULL
);
1517 ok(SUCCEEDED(r
), "CoInitialize failed (0x%08x)\n", r
);
1521 rc
=GetModuleFileName(NULL
, argv0
, sizeof(argv0
));
1522 assert(rc
!=0 && rc
<sizeof(argv0
));
1523 if (GetFileAttributes(argv0
)==INVALID_FILE_ATTRIBUTES
)
1525 strcat(argv0
, ".so");
1526 ok(GetFileAttributes(argv0
)!=INVALID_FILE_ATTRIBUTES
,
1527 "unable to find argv0!\n");
1530 GetTempPathA(sizeof(tmpdir
)/sizeof(*tmpdir
), tmpdir
);
1531 assert(GetTempFileNameA(tmpdir
, "wt", 0, child_file
)!=0);
1532 init_event(child_file
);
1534 /* Set up the test files */
1540 sprintf(filename
, *testfile
, tmpdir
);
1541 hfile
=CreateFile(filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
,
1542 FILE_ATTRIBUTE_NORMAL
, NULL
);
1543 if (hfile
==INVALID_HANDLE_VALUE
)
1545 trace("unable to create '%s': err=%d\n", filename
, GetLastError());
1552 /* Setup the test shortcuts */
1553 sprintf(filename
, "%s\\test_shortcut_shlexec.lnk", tmpdir
);
1554 MultiByteToWideChar(CP_ACP
, 0, filename
, -1, lnkfile
, sizeof(lnkfile
)/sizeof(*lnkfile
));
1555 desc
.description
=NULL
;
1557 sprintf(filename
, "%s\\test file.shlexec", tmpdir
);
1560 desc
.arguments
="ignored";
1565 create_lnk(lnkfile
, &desc
, 0);
1567 sprintf(filename
, "%s\\test_shortcut_exe.lnk", tmpdir
);
1568 MultiByteToWideChar(CP_ACP
, 0, filename
, -1, lnkfile
, sizeof(lnkfile
)/sizeof(*lnkfile
));
1569 desc
.description
=NULL
;
1573 sprintf(params
, "shlexec \"%s\" Lnk", child_file
);
1574 desc
.arguments
=params
;
1579 create_lnk(lnkfile
, &desc
, 0);
1581 /* Create a basic association suitable for most tests */
1582 if (!create_test_association(".shlexec"))
1584 skip("Unable to create association for '.shlexec'\n");
1587 create_test_verb(".shlexec", "Open", 0, "Open \"%1\"");
1588 create_test_verb(".shlexec", "NoQuotes", 0, "NoQuotes %1");
1589 create_test_verb(".shlexec", "LowerL", 0, "LowerL %l");
1590 create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
1591 create_test_verb(".shlexec", "UpperL", 0, "UpperL %L");
1592 create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
1595 static void cleanup_test(void)
1597 char filename
[MAX_PATH
];
1598 const char* const * testfile
;
1600 /* Delete the test files */
1604 sprintf(filename
, *testfile
, tmpdir
);
1605 /* Make sure we can delete the files ('test file.noassoc' is read-only now) */
1606 SetFileAttributes(filename
, FILE_ATTRIBUTE_NORMAL
);
1607 DeleteFile(filename
);
1610 DeleteFile(child_file
);
1612 /* Delete the test association */
1613 delete_test_association(".shlexec");
1615 CloseHandle(hEvent
);
1620 static void test_commandline(void)
1622 static const WCHAR one
[] = {'o','n','e',0};
1623 static const WCHAR two
[] = {'t','w','o',0};
1624 static const WCHAR three
[] = {'t','h','r','e','e',0};
1625 static const WCHAR four
[] = {'f','o','u','r',0};
1627 static const WCHAR fmt1
[] = {'%','s',' ','%','s',' ','%','s',' ','%','s',0};
1628 static const WCHAR fmt2
[] = {' ','%','s',' ','%','s',' ','%','s',' ','%','s',0};
1629 static const WCHAR fmt3
[] = {'%','s','=','%','s',' ','%','s','=','\"','%','s','\"',0};
1630 static const WCHAR fmt4
[] = {'\"','%','s','\"',' ','\"','%','s',' ','%','s','\"',' ','%','s',0};
1631 static const WCHAR fmt5
[] = {'\\','\"','%','s','\"',' ','%','s','=','\"','%','s','\\','\"',' ','\"','%','s','\\','\"',0};
1632 static const WCHAR fmt6
[] = {0};
1634 static const WCHAR chkfmt1
[] = {'%','s','=','%','s',0};
1635 static const WCHAR chkfmt2
[] = {'%','s',' ','%','s',0};
1636 static const WCHAR chkfmt3
[] = {'\\','\"','%','s','\"',0};
1637 static const WCHAR chkfmt4
[] = {'%','s','=','%','s','\"',' ','%','s','\"',0};
1639 LPWSTR
*args
= (LPWSTR
*)0xdeadcafe;
1642 wsprintfW(cmdline
,fmt1
,one
,two
,three
,four
);
1643 args
=CommandLineToArgvW(cmdline
,&numargs
);
1644 if (args
== NULL
&& numargs
== -1)
1646 win_skip("CommandLineToArgvW not implemented, skipping\n");
1649 ok(numargs
== 4, "expected 4 args, got %i\n",numargs
);
1650 ok(lstrcmpW(args
[0],one
)==0,"arg0 is not as expected\n");
1651 ok(lstrcmpW(args
[1],two
)==0,"arg1 is not as expected\n");
1652 ok(lstrcmpW(args
[2],three
)==0,"arg2 is not as expected\n");
1653 ok(lstrcmpW(args
[3],four
)==0,"arg3 is not as expected\n");
1655 wsprintfW(cmdline
,fmt2
,one
,two
,three
,four
);
1656 args
=CommandLineToArgvW(cmdline
,&numargs
);
1657 ok(numargs
== 5, "expected 5 args, got %i\n",numargs
);
1658 ok(args
[0][0]==0,"arg0 is not as expected\n");
1659 ok(lstrcmpW(args
[1],one
)==0,"arg1 is not as expected\n");
1660 ok(lstrcmpW(args
[2],two
)==0,"arg2 is not as expected\n");
1661 ok(lstrcmpW(args
[3],three
)==0,"arg3 is not as expected\n");
1662 ok(lstrcmpW(args
[4],four
)==0,"arg4 is not as expected\n");
1664 wsprintfW(cmdline
,fmt3
,one
,two
,three
,four
);
1665 args
=CommandLineToArgvW(cmdline
,&numargs
);
1666 ok(numargs
== 2, "expected 2 args, got %i\n",numargs
);
1667 wsprintfW(cmdline
,chkfmt1
,one
,two
);
1668 ok(lstrcmpW(args
[0],cmdline
)==0,"arg0 is not as expected\n");
1669 wsprintfW(cmdline
,chkfmt1
,three
,four
);
1670 ok(lstrcmpW(args
[1],cmdline
)==0,"arg1 is not as expected\n");
1672 wsprintfW(cmdline
,fmt4
,one
,two
,three
,four
);
1673 args
=CommandLineToArgvW(cmdline
,&numargs
);
1674 ok(numargs
== 3, "expected 3 args, got %i\n",numargs
);
1675 ok(lstrcmpW(args
[0],one
)==0,"arg0 is not as expected\n");
1676 wsprintfW(cmdline
,chkfmt2
,two
,three
);
1677 ok(lstrcmpW(args
[1],cmdline
)==0,"arg1 is not as expected\n");
1678 ok(lstrcmpW(args
[2],four
)==0,"arg2 is not as expected\n");
1680 wsprintfW(cmdline
,fmt5
,one
,two
,three
,four
);
1681 args
=CommandLineToArgvW(cmdline
,&numargs
);
1682 ok(numargs
== 2, "expected 2 args, got %i\n",numargs
);
1683 wsprintfW(cmdline
,chkfmt3
,one
);
1684 todo_wine
ok(lstrcmpW(args
[0],cmdline
)==0,"arg0 is not as expected\n");
1685 wsprintfW(cmdline
,chkfmt4
,two
,three
,four
);
1686 todo_wine
ok(lstrcmpW(args
[1],cmdline
)==0,"arg1 is not as expected\n");
1688 wsprintfW(cmdline
,fmt6
);
1689 args
=CommandLineToArgvW(cmdline
,&numargs
);
1690 ok(numargs
== 1, "expected 1 args, got %i\n",numargs
);
1696 myARGC
= winetest_get_mainargs(&myARGV
);
1699 doChild(myARGC
, myARGV
);
1706 test_find_executable();
1711 test_dde_default_app();