urlmon: Don't create stgmed_obj for binding to object.
[wine] / dlls / shell32 / tests / shlexec.c
1 /*
2  * Unit test of the ShellExecute function.
3  *
4  * Copyright 2005 Francois Gouget for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
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
33  */
34
35 #include <stdio.h>
36 #include <assert.h>
37
38 /* Needed to get SEE_MASK_NOZONECHECKS with the PSDK */
39 #define NTDDI_WINXPSP1 0x05010100
40 #define NTDDI_VERSION NTDDI_WINXPSP1
41
42 #include "wtypes.h"
43 #include "winbase.h"
44 #include "windef.h"
45 #include "shellapi.h"
46 #include "shlwapi.h"
47 #include "wine/test.h"
48
49 #include "shell32_test.h"
50
51
52 static char argv0[MAX_PATH];
53 static int myARGC;
54 static char** myARGV;
55 static char tmpdir[MAX_PATH];
56 static char child_file[MAX_PATH];
57 static DLLVERSIONINFO dllver;
58
59
60 /***
61  *
62  * ShellExecute wrappers
63  *
64  ***/
65 static void dump_child(void);
66
67 static HANDLE hEvent;
68 static void init_event(const char* child_file)
69 {
70     char* event_name;
71     event_name=strrchr(child_file, '\\')+1;
72     hEvent=CreateEvent(NULL, FALSE, FALSE, event_name);
73 }
74
75 static void strcat_param(char* str, const char* param)
76 {
77     if (param!=NULL)
78     {
79         strcat(str, "\"");
80         strcat(str, param);
81         strcat(str, "\"");
82     }
83     else
84     {
85         strcat(str, "null");
86     }
87 }
88
89 static char shell_call[2048]="";
90 static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
91 {
92     int rc;
93
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);
105
106     DeleteFile(child_file);
107     SetLastError(0xcafebabe);
108
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.
112      */
113     rc=(int)ShellExecute(NULL, operation, file, parameters, directory,
114                          SW_SHOWNORMAL);
115
116     if (rc > 32)
117     {
118         int wait_rc;
119         wait_rc=WaitForSingleObject(hEvent, 5000);
120         ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
121     }
122     /* The child process may have changed the result file, so let profile
123      * functions know about it
124      */
125     WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
126     if (rc > 32)
127         dump_child();
128
129     return rc;
130 }
131
132 static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
133                             LPCSTR parameters, LPCSTR directory)
134 {
135     SHELLEXECUTEINFO sei;
136     BOOL success;
137     int rc;
138
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);
150
151     sei.cbSize=sizeof(sei);
152     sei.fMask=SEE_MASK_NOCLOSEPROCESS | mask;
153     sei.hwnd=NULL;
154     sei.lpVerb=operation;
155     sei.lpFile=file;
156     sei.lpParameters=parameters;
157     sei.lpDirectory=directory;
158     sei.nShow=SW_SHOWNORMAL;
159     sei.hInstApp=NULL; /* Out */
160     sei.lpIDList=NULL;
161     sei.lpClass=NULL;
162     sei.hkeyClass=NULL;
163     sei.dwHotKey=0;
164     U(sei).hIcon=NULL;
165     sei.hProcess=NULL; /* Out */
166
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);
173
174     if (rc > 32)
175     {
176         int wait_rc;
177         if (sei.hProcess!=NULL)
178         {
179             wait_rc=WaitForSingleObject(sei.hProcess, 5000);
180             ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject(hProcess) returned %d\n", wait_rc);
181         }
182         wait_rc=WaitForSingleObject(hEvent, 5000);
183         ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
184     }
185     /* The child process may have changed the result file, so let profile
186      * functions know about it
187      */
188     WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
189     if (rc > 32)
190         dump_child();
191
192     return rc;
193 }
194
195
196
197 /***
198  *
199  * Functions to create / delete associations wrappers
200  *
201  ***/
202
203 static void create_test_association(const char* extension)
204 {
205     HKEY hkey, hkey_shell;
206     char class[MAX_PATH];
207     LONG rc;
208
209     sprintf(class, "shlexec%s", extension);
210     rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
211                       NULL, &hkey, NULL);
212     assert(rc==ERROR_SUCCESS);
213     rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, (LPBYTE) class, strlen(class)+1);
214     assert(rc==ERROR_SUCCESS);
215     CloseHandle(hkey);
216
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);
223     CloseHandle(hkey);
224     CloseHandle(hkey_shell);
225 }
226
227 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
228 static LSTATUS myRegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
229 {
230     LONG ret;
231     DWORD dwMaxSubkeyLen, dwMaxValueLen;
232     DWORD dwMaxLen, dwSize;
233     CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
234     HKEY hSubKey = hKey;
235
236     if(lpszSubKey)
237     {
238         ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
239         if (ret) return ret;
240     }
241
242     /* Get highest length for keys, values */
243     ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
244             &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
245     if (ret) goto cleanup;
246
247     dwMaxSubkeyLen++;
248     dwMaxValueLen++;
249     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
250     if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
251     {
252         /* Name too big: alloc a buffer for it */
253         if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
254         {
255             ret = ERROR_NOT_ENOUGH_MEMORY;
256             goto cleanup;
257         }
258     }
259
260
261     /* Recursively delete all the subkeys */
262     while (TRUE)
263     {
264         dwSize = dwMaxLen;
265         if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
266                           NULL, NULL, NULL)) break;
267
268         ret = myRegDeleteTreeA(hSubKey, lpszName);
269         if (ret) goto cleanup;
270     }
271
272     if (lpszSubKey)
273         ret = RegDeleteKeyA(hKey, lpszSubKey);
274     else
275         while (TRUE)
276         {
277             dwSize = dwMaxLen;
278             if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
279                   NULL, NULL, NULL, NULL)) break;
280
281             ret = RegDeleteValueA(hKey, lpszName);
282             if (ret) goto cleanup;
283         }
284
285 cleanup:
286     /* Free buffer if allocated */
287     if (lpszName != szNameBuf)
288         HeapFree( GetProcessHeap(), 0, lpszName);
289     if(lpszSubKey)
290         RegCloseKey(hSubKey);
291     return ret;
292 }
293
294 static void delete_test_association(const char* extension)
295 {
296     char class[MAX_PATH];
297
298     sprintf(class, "shlexec%s", extension);
299     myRegDeleteTreeA(HKEY_CLASSES_ROOT, class);
300     myRegDeleteTreeA(HKEY_CLASSES_ROOT, extension);
301 }
302
303 static void create_test_verb_dde(const char* extension, const char* verb,
304                                  int rawcmd, const char* cmdtail, const char *ddeexec,
305                                  const char *application, const char *topic,
306                                  const char *ifexec)
307 {
308     HKEY hkey_shell, hkey_verb, hkey_cmd;
309     char shell[MAX_PATH];
310     char* cmd;
311     LONG rc;
312
313     sprintf(shell, "shlexec%s\\shell", extension);
314     rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
315                     KEY_CREATE_SUB_KEY, &hkey_shell);
316     assert(rc==ERROR_SUCCESS);
317     rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
318                       NULL, &hkey_verb, NULL);
319     assert(rc==ERROR_SUCCESS);
320     rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
321                       NULL, &hkey_cmd, NULL);
322     assert(rc==ERROR_SUCCESS);
323
324     if (rawcmd)
325     {
326         rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmdtail, strlen(cmdtail)+1);
327     }
328     else
329     {
330         cmd=malloc(strlen(argv0)+10+strlen(child_file)+2+strlen(cmdtail)+1);
331         sprintf(cmd,"%s shlexec \"%s\" %s", argv0, child_file, cmdtail);
332         rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmd, strlen(cmd)+1);
333         assert(rc==ERROR_SUCCESS);
334         free(cmd);
335     }
336
337     if (ddeexec)
338     {
339         HKEY hkey_ddeexec, hkey_application, hkey_topic, hkey_ifexec;
340
341         rc=RegCreateKeyEx(hkey_verb, "ddeexec", 0, NULL, 0, KEY_SET_VALUE |
342                           KEY_CREATE_SUB_KEY, NULL, &hkey_ddeexec, NULL);
343         assert(rc==ERROR_SUCCESS);
344         rc=RegSetValueEx(hkey_ddeexec, NULL, 0, REG_SZ, (LPBYTE)ddeexec,
345                          strlen(ddeexec)+1);
346         assert(rc==ERROR_SUCCESS);
347         if (application)
348         {
349             rc=RegCreateKeyEx(hkey_ddeexec, "application", 0, NULL, 0, KEY_SET_VALUE,
350                               NULL, &hkey_application, NULL);
351             assert(rc==ERROR_SUCCESS);
352             rc=RegSetValueEx(hkey_application, NULL, 0, REG_SZ, (LPBYTE)application,
353                              strlen(application)+1);
354             assert(rc==ERROR_SUCCESS);
355             CloseHandle(hkey_application);
356         }
357         if (topic)
358         {
359             rc=RegCreateKeyEx(hkey_ddeexec, "topic", 0, NULL, 0, KEY_SET_VALUE,
360                               NULL, &hkey_topic, NULL);
361             assert(rc==ERROR_SUCCESS);
362             rc=RegSetValueEx(hkey_topic, NULL, 0, REG_SZ, (LPBYTE)topic,
363                              strlen(topic)+1);
364             assert(rc==ERROR_SUCCESS);
365             CloseHandle(hkey_topic);
366         }
367         if (ifexec)
368         {
369             rc=RegCreateKeyEx(hkey_ddeexec, "ifexec", 0, NULL, 0, KEY_SET_VALUE,
370                               NULL, &hkey_ifexec, NULL);
371             assert(rc==ERROR_SUCCESS);
372             rc=RegSetValueEx(hkey_ifexec, NULL, 0, REG_SZ, (LPBYTE)ifexec,
373                              strlen(ifexec)+1);
374             assert(rc==ERROR_SUCCESS);
375             CloseHandle(hkey_ifexec);
376         }
377         CloseHandle(hkey_ddeexec);
378     }
379
380     CloseHandle(hkey_shell);
381     CloseHandle(hkey_verb);
382     CloseHandle(hkey_cmd);
383 }
384
385 static void create_test_verb(const char* extension, const char* verb,
386                              int rawcmd, const char* cmdtail)
387 {
388     create_test_verb_dde(extension, verb, rawcmd, cmdtail, NULL, NULL,
389                          NULL, NULL);
390 }
391
392 /***
393  *
394  * Functions to check that the child process was started just right
395  * (borrowed from dlls/kernel32/tests/process.c)
396  *
397  ***/
398
399 static const char* encodeA(const char* str)
400 {
401     static char encoded[2*1024+1];
402     char*       ptr;
403     size_t      len,i;
404
405     if (!str) return "";
406     len = strlen(str) + 1;
407     if (len >= sizeof(encoded)/2)
408     {
409         fprintf(stderr, "string is too long!\n");
410         assert(0);
411     }
412     ptr = encoded;
413     for (i = 0; i < len; i++)
414         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
415     ptr[2 * len] = '\0';
416     return ptr;
417 }
418
419 static unsigned decode_char(char c)
420 {
421     if (c >= '0' && c <= '9') return c - '0';
422     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
423     assert(c >= 'A' && c <= 'F');
424     return c - 'A' + 10;
425 }
426
427 static char* decodeA(const char* str)
428 {
429     static char decoded[1024];
430     char*       ptr;
431     size_t      len,i;
432
433     len = strlen(str) / 2;
434     if (!len--) return NULL;
435     if (len >= sizeof(decoded))
436     {
437         fprintf(stderr, "string is too long!\n");
438         assert(0);
439     }
440     ptr = decoded;
441     for (i = 0; i < len; i++)
442         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
443     ptr[len] = '\0';
444     return ptr;
445 }
446
447 static void     childPrintf(HANDLE h, const char* fmt, ...)
448 {
449     va_list     valist;
450     char        buffer[1024];
451     DWORD       w;
452
453     va_start(valist, fmt);
454     vsprintf(buffer, fmt, valist);
455     va_end(valist);
456     WriteFile(h, buffer, strlen(buffer), &w, NULL);
457 }
458
459 static void doChild(int argc, char** argv)
460 {
461     char* filename;
462     HANDLE hFile;
463     int i;
464
465     filename=argv[2];
466     hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
467     if (hFile == INVALID_HANDLE_VALUE)
468         return;
469
470     /* Arguments */
471     childPrintf(hFile, "[Arguments]\r\n");
472     if (winetest_debug > 2)
473         trace("argcA=%d\n", argc);
474     childPrintf(hFile, "argcA=%d\r\n", argc);
475     for (i = 0; i < argc; i++)
476     {
477         if (winetest_debug > 2)
478             trace("argvA%d=%s\n", i, argv[i]);
479         childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i]));
480     }
481     CloseHandle(hFile);
482
483     init_event(filename);
484     SetEvent(hEvent);
485     CloseHandle(hEvent);
486 }
487
488 static char* getChildString(const char* sect, const char* key)
489 {
490     char        buf[1024];
491     char*       ret;
492
493     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file);
494     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
495     assert(!(strlen(buf) & 1));
496     ret = decodeA(buf);
497     return ret;
498 }
499
500 static void dump_child(void)
501 {
502     if (winetest_debug > 1)
503     {
504         char key[18];
505         char* str;
506         int i, c;
507
508         c=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file);
509         trace("argcA=%d\n",c);
510         for (i=0;i<c;i++)
511         {
512             sprintf(key, "argvA%d", i);
513             str=getChildString("Arguments", key);
514             trace("%s=%s\n", key, str);
515         }
516     }
517 }
518
519 static int StrCmpPath(const char* s1, const char* s2)
520 {
521     if (!s1 && !s2) return 0;
522     if (!s2) return 1;
523     if (!s1) return -1;
524     while (*s1)
525     {
526         if (!*s2)
527         {
528             if (*s1=='.')
529                 s1++;
530             return (*s1-*s2);
531         }
532         if ((*s1=='/' || *s1=='\\') && (*s2=='/' || *s2=='\\'))
533         {
534             while (*s1=='/' || *s1=='\\')
535                 s1++;
536             while (*s2=='/' || *s2=='\\')
537                 s2++;
538         }
539         else if (toupper(*s1)==toupper(*s2))
540         {
541             s1++;
542             s2++;
543         }
544         else
545         {
546             return (*s1-*s2);
547         }
548     }
549     if (*s2=='.')
550         s2++;
551     if (*s2)
552         return -1;
553     return 0;
554 }
555
556 static int _okChildString(const char* file, int line, const char* key, const char* expected)
557 {
558     char* result;
559     result=getChildString("Arguments", key);
560     return ok_(file, line)(lstrcmpiA(result, expected) == 0,
561                "%s expected '%s', got '%s'\n", key, expected, result);
562 }
563
564 static int _okChildPath(const char* file, int line, const char* key, const char* expected)
565 {
566     char* result;
567     result=getChildString("Arguments", key);
568     return ok_(file, line)(StrCmpPath(result, expected) == 0,
569                "%s expected '%s', got '%s'\n", key, expected, result);
570 }
571
572 static int _okChildInt(const char* file, int line, const char* key, int expected)
573 {
574     INT result;
575     result=GetPrivateProfileIntA("Arguments", key, expected, child_file);
576     return ok_(file, line)(result == expected,
577                "%s expected %d, but got %d\n", key, expected, result);
578 }
579
580 #define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
581 #define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
582 #define okChildInt(key, expected)    _okChildInt(__FILE__, __LINE__, (key), (expected))
583
584
585
586 /***
587  *
588  * Tests
589  *
590  ***/
591
592 static const char* testfiles[]=
593 {
594     "%s\\test file.shlexec",
595     "%s\\%%nasty%% $file.shlexec",
596     "%s\\test file.noassoc",
597     "%s\\test file.noassoc.shlexec",
598     "%s\\test file.shlexec.noassoc",
599     "%s\\test_shortcut_shlexec.lnk",
600     "%s\\test_shortcut_exe.lnk",
601     "%s\\test file.shl",
602     "%s\\test file.shlfoo",
603     "%s\\test file.sfe",
604     "%s\\masked file.shlexec",
605     "%s\\masked",
606     "%s\\test file.sde",
607     "%s\\test file.exe",
608     "%s\\test2.exe",
609     NULL
610 };
611
612 typedef struct
613 {
614     const char* verb;
615     const char* basename;
616     int todo;
617     int rc;
618 } filename_tests_t;
619
620 static filename_tests_t filename_tests[]=
621 {
622     /* Test bad / nonexistent filenames */
623     {NULL,           "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF},
624     {NULL,           "%s\\nonexistent.noassoc", 0x11, SE_ERR_FNF},
625
626     /* Standard tests */
627     {NULL,           "%s\\test file.shlexec",   0x0, 33},
628     {NULL,           "%s\\test file.shlexec.",  0x0, 33},
629     {NULL,           "%s\\%%nasty%% $file.shlexec", 0x0, 33},
630     {NULL,           "%s/test file.shlexec",    0x0, 33},
631
632     /* Test filenames with no association */
633     {NULL,           "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
634
635     /* Test double extensions */
636     {NULL,           "%s\\test file.noassoc.shlexec", 0x0, 33},
637     {NULL,           "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC},
638
639     /* Test alternate verbs */
640     {"LowerL",       "%s\\nonexistent.shlexec", 0x11, SE_ERR_FNF},
641     {"LowerL",       "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
642
643     {"QuotedLowerL", "%s\\test file.shlexec",   0x0, 33},
644     {"QuotedUpperL", "%s\\test file.shlexec",   0x0, 33},
645
646     /* Test file masked due to space */
647     {NULL,           "%s\\masked file.shlexec",   0x1, 33},
648     /* Test if quoting prevents the masking */
649     {NULL,           "%s\\masked file.shlexec",   0x40, 33},
650
651     {NULL, NULL, 0}
652 };
653
654 static filename_tests_t noquotes_tests[]=
655 {
656     /* Test unquoted '%1' thingies */
657     {"NoQuotes",     "%s\\test file.shlexec",   0xa, 33},
658     {"LowerL",       "%s\\test file.shlexec",   0xa, 33},
659     {"UpperL",       "%s\\test file.shlexec",   0xa, 33},
660
661     {NULL, NULL, 0}
662 };
663
664 static void test_filename(void)
665 {
666     char filename[MAX_PATH];
667     const filename_tests_t* test;
668     char* c;
669     int rc;
670
671     test=filename_tests;
672     while (test->basename)
673     {
674         sprintf(filename, test->basename, tmpdir);
675         if (strchr(filename, '/'))
676         {
677             c=filename;
678             while (*c)
679             {
680                 if (*c=='\\')
681                     *c='/';
682                 c++;
683             }
684         }
685         if ((test->todo & 0x40)==0)
686         {
687             rc=shell_execute(test->verb, filename, NULL, NULL);
688         }
689         else
690         {
691             char quoted[MAX_PATH + 2];
692             sprintf(quoted, "\"%s\"", filename);
693             rc=shell_execute(test->verb, quoted, NULL, NULL);
694         }
695         if (rc > 32)
696             rc=33;
697         if ((test->todo & 0x1)==0)
698         {
699             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
700                rc, GetLastError());
701         }
702         else todo_wine
703         {
704             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
705                rc, GetLastError());
706         }
707         if (rc == 33)
708         {
709             const char* verb;
710             if ((test->todo & 0x2)==0)
711             {
712                 okChildInt("argcA", 5);
713             }
714             else todo_wine
715             {
716                 okChildInt("argcA", 5);
717             }
718             verb=(test->verb ? test->verb : "Open");
719             if ((test->todo & 0x4)==0)
720             {
721                 okChildString("argvA3", verb);
722             }
723             else todo_wine
724             {
725                 okChildString("argvA3", verb);
726             }
727             if ((test->todo & 0x8)==0)
728             {
729                 okChildPath("argvA4", filename);
730             }
731             else todo_wine
732             {
733                 okChildPath("argvA4", filename);
734             }
735         }
736         test++;
737     }
738
739     test=noquotes_tests;
740     while (test->basename)
741     {
742         sprintf(filename, test->basename, tmpdir);
743         rc=shell_execute(test->verb, filename, NULL, NULL);
744         if (rc > 32)
745             rc=33;
746         if ((test->todo & 0x1)==0)
747         {
748             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
749                rc, GetLastError());
750         }
751         else todo_wine
752         {
753             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
754                rc, GetLastError());
755         }
756         if (rc==0)
757         {
758             int count;
759             const char* verb;
760             char* str;
761
762             verb=(test->verb ? test->verb : "Open");
763             if ((test->todo & 0x4)==0)
764             {
765                 okChildString("argvA3", verb);
766             }
767             else todo_wine
768             {
769                 okChildString("argvA3", verb);
770             }
771
772             count=4;
773             str=filename;
774             while (1)
775             {
776                 char attrib[18];
777                 char* space;
778                 space=strchr(str, ' ');
779                 if (space)
780                     *space='\0';
781                 sprintf(attrib, "argvA%d", count);
782                 if ((test->todo & 0x8)==0)
783                 {
784                     okChildPath(attrib, str);
785                 }
786                 else todo_wine
787                 {
788                     okChildPath(attrib, str);
789                 }
790                 count++;
791                 if (!space)
792                     break;
793                 str=space+1;
794             }
795             if ((test->todo & 0x2)==0)
796             {
797                 okChildInt("argcA", count);
798             }
799             else todo_wine
800             {
801                 okChildInt("argcA", count);
802             }
803         }
804         test++;
805     }
806
807     if (dllver.dwMajorVersion != 0)
808     {
809         /* The more recent versions of shell32.dll accept quoted filenames
810          * while older ones (e.g. 4.00) don't. Still we want to test this
811          * because IE 6 depends on the new behavior.
812          * One day we may need to check the exact version of the dll but for
813          * now making sure DllGetVersion() is present is sufficient.
814          */
815         sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
816         rc=shell_execute(NULL, filename, NULL, NULL);
817         ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
818            GetLastError());
819         okChildInt("argcA", 5);
820         okChildString("argvA3", "Open");
821         sprintf(filename, "%s\\test file.shlexec", tmpdir);
822         okChildPath("argvA4", filename);
823     }
824 }
825
826 static void test_find_executable(void)
827 {
828     char filename[MAX_PATH];
829     char command[MAX_PATH];
830     const filename_tests_t* test;
831     int rc;
832
833     create_test_association(".sfe");
834     create_test_verb(".sfe", "Open", 1, "%1");
835
836     /* Don't test FindExecutable(..., NULL), it always crashes */
837
838     strcpy(command, "your word");
839     rc=(int)FindExecutableA(NULL, NULL, command);
840     ok(rc == SE_ERR_FNF || rc > 32 /* nt4 */, "FindExecutable(NULL) returned %d\n", rc);
841     ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
842
843     strcpy(command, "your word");
844     rc=(int)FindExecutableA(tmpdir, NULL, command);
845     ok(rc == SE_ERR_NOASSOC /* >= win2000 */ || rc > 32 /* win98, nt4 */, "FindExecutable(NULL) returned %d\n", rc);
846     ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
847
848     sprintf(filename, "%s\\test file.sfe", tmpdir);
849     rc=(int)FindExecutableA(filename, NULL, command);
850     ok(rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
851     /* Depending on the platform, command could be '%1' or 'test file.sfe' */
852
853     rc=(int)FindExecutableA("test file.sfe", tmpdir, command);
854     ok(rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
855
856     rc=(int)FindExecutableA("test file.sfe", NULL, command);
857     todo_wine ok(rc == SE_ERR_FNF, "FindExecutable(%s) returned %d\n", filename, rc);
858
859     delete_test_association(".sfe");
860
861     create_test_association(".shl");
862     create_test_verb(".shl", "Open", 0, "Open");
863
864     sprintf(filename, "%s\\test file.shl", tmpdir);
865     rc=(int)FindExecutableA(filename, NULL, command);
866     ok(rc == SE_ERR_FNF /* NT4 */ || rc > 32, "FindExecutable(%s) returned %d\n", filename, rc);
867
868     sprintf(filename, "%s\\test file.shlfoo", tmpdir);
869     rc=(int)FindExecutableA(filename, NULL, command);
870
871     delete_test_association(".shl");
872
873     if (rc > 32)
874     {
875         /* On Windows XP and 2003 FindExecutable() is completely broken.
876          * Probably what it does is convert the filename to 8.3 format,
877          * which as a side effect converts the '.shlfoo' extension to '.shl',
878          * and then tries to find an association for '.shl'. This means it
879          * will normally fail on most extensions with more than 3 characters,
880          * like '.mpeg', etc.
881          * Also it means we cannot do any other test.
882          */
883         trace("FindExecutable() is broken -> skipping 4+ character extension tests\n");
884         return;
885     }
886
887     test=filename_tests;
888     while (test->basename)
889     {
890         sprintf(filename, test->basename, tmpdir);
891         if (strchr(filename, '/'))
892         {
893             char* c;
894             c=filename;
895             while (*c)
896             {
897                 if (*c=='\\')
898                     *c='/';
899                 c++;
900             }
901         }
902         /* Win98 does not '\0'-terminate command! */
903         memset(command, '\0', sizeof(command));
904         rc=(int)FindExecutableA(filename, NULL, command);
905         if (rc > 32)
906             rc=33;
907         if ((test->todo & 0x10)==0)
908         {
909             ok(rc==test->rc, "FindExecutable(%s) failed: rc=%d\n", filename, rc);
910         }
911         else todo_wine
912         {
913             ok(rc==test->rc, "FindExecutable(%s) failed: rc=%d\n", filename, rc);
914         }
915         if (rc > 32)
916         {
917             int equal;
918             equal=strcmp(command, argv0) == 0 ||
919                 /* NT4 returns an extra 0x8 character! */
920                 (strlen(command) == strlen(argv0)+1 && strncmp(command, argv0, strlen(argv0)) == 0);
921             if ((test->todo & 0x20)==0)
922             {
923                 ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
924                    filename, command, argv0);
925             }
926             else todo_wine
927             {
928                 ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
929                    filename, command, argv0);
930             }
931         }
932         test++;
933     }
934 }
935
936
937 static filename_tests_t lnk_tests[]=
938 {
939     /* Pass bad / nonexistent filenames as a parameter */
940     {NULL, "%s\\nonexistent.shlexec",    0xa, 33},
941     {NULL, "%s\\nonexistent.noassoc",    0xa, 33},
942
943     /* Pass regular paths as a parameter */
944     {NULL, "%s\\test file.shlexec",      0xa, 33},
945     {NULL, "%s/%%nasty%% $file.shlexec", 0xa, 33},
946
947     /* Pass filenames with no association as a parameter */
948     {NULL, "%s\\test file.noassoc",      0xa, 33},
949
950     {NULL, NULL, 0}
951 };
952
953 static void test_lnks(void)
954 {
955     char filename[MAX_PATH];
956     char params[MAX_PATH];
957     const filename_tests_t* test;
958     int rc;
959
960     sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
961     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
962     ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
963        GetLastError());
964     okChildInt("argcA", 5);
965     okChildString("argvA3", "Open");
966     sprintf(filename, "%s\\test file.shlexec", tmpdir);
967     okChildPath("argvA4", filename);
968
969     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
970     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
971     ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
972        GetLastError());
973     okChildInt("argcA", 4);
974     okChildString("argvA3", "Lnk");
975
976     if (dllver.dwMajorVersion>=6)
977     {
978         char* c;
979        /* Recent versions of shell32.dll accept '/'s in shortcut paths.
980          * Older versions don't or are quite buggy in this regard.
981          */
982         sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
983         c=filename;
984         while (*c)
985         {
986             if (*c=='\\')
987                 *c='/';
988             c++;
989         }
990         rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
991         ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
992            GetLastError());
993         okChildInt("argcA", 4);
994         okChildString("argvA3", "Lnk");
995     }
996
997     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
998     test=lnk_tests;
999     while (test->basename)
1000     {
1001         params[0]='\"';
1002         sprintf(params+1, test->basename, tmpdir);
1003         strcat(params,"\"");
1004         rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, params,
1005                             NULL);
1006         if (rc > 32)
1007             rc=33;
1008         if ((test->todo & 0x1)==0)
1009         {
1010             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1011                rc, GetLastError());
1012         }
1013         else todo_wine
1014         {
1015             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1016                rc, GetLastError());
1017         }
1018         if (rc==0)
1019         {
1020             if ((test->todo & 0x2)==0)
1021             {
1022                 okChildInt("argcA", 5);
1023             }
1024             else 
1025             {
1026                 okChildInt("argcA", 5);
1027             }
1028             if ((test->todo & 0x4)==0)
1029             {
1030                 okChildString("argvA3", "Lnk");
1031             }
1032             else todo_wine
1033             {
1034                 okChildString("argvA3", "Lnk");
1035             }
1036             sprintf(params, test->basename, tmpdir);
1037             if ((test->todo & 0x8)==0)
1038             {
1039                 okChildPath("argvA4", params);
1040             }
1041             else
1042             {
1043                 okChildPath("argvA4", params);
1044             }
1045         }
1046         test++;
1047     }
1048 }
1049
1050
1051 static void test_exes(void)
1052 {
1053     char filename[MAX_PATH];
1054     char params[1024];
1055     int rc;
1056
1057     sprintf(params, "shlexec \"%s\" Exec", child_file);
1058
1059     /* We need NOZONECHECKS on Win2003 to block a dialog */
1060     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
1061                         NULL);
1062     ok(rc > 32, "%s returned %d\n", shell_call, rc);
1063     okChildInt("argcA", 4);
1064     okChildString("argvA3", "Exec");
1065
1066     sprintf(filename, "%s\\test file.noassoc", tmpdir);
1067     if (CopyFile(argv0, filename, FALSE))
1068     {
1069         rc=shell_execute(NULL, filename, params, NULL);
1070         todo_wine {
1071         ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
1072         }
1073     }
1074 }
1075
1076 static void test_exes_long(void)
1077 {
1078     char filename[MAX_PATH];
1079     char params[2024];
1080     char longparam[MAX_PATH];
1081     int rc;
1082
1083     for (rc = 0; rc < MAX_PATH; rc++)
1084         longparam[rc]='a'+rc%26;
1085     longparam[MAX_PATH-1]=0;
1086
1087
1088     sprintf(params, "shlexec \"%s\" %s", child_file,longparam);
1089
1090     /* We need NOZONECHECKS on Win2003 to block a dialog */
1091     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
1092                         NULL);
1093     ok(rc > 32, "%s returned %d\n", shell_call, rc);
1094     okChildInt("argcA", 4);
1095     okChildString("argvA3", longparam);
1096
1097     sprintf(filename, "%s\\test file.noassoc", tmpdir);
1098     if (CopyFile(argv0, filename, FALSE))
1099     {
1100         rc=shell_execute(NULL, filename, params, NULL);
1101         todo_wine {
1102         ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
1103         }
1104     }
1105 }
1106
1107 typedef struct
1108 {
1109     const char* command;
1110     const char* ddeexec;
1111     const char* application;
1112     const char* topic;
1113     const char* ifexec;
1114     int expectedArgs;
1115     const char* expectedDdeExec;
1116     int todo;
1117     int rc;
1118 } dde_tests_t;
1119
1120 static dde_tests_t dde_tests[] =
1121 {
1122     /* Test passing and not passing command-line
1123      * argument, no DDE */
1124     {"", NULL, NULL, NULL, NULL, FALSE, "", 0x0, 33},
1125     {"\"%1\"", NULL, NULL, NULL, NULL, TRUE, "", 0x0, 33},
1126
1127     /* Test passing and not passing command-line
1128      * argument, with DDE */
1129     {"", "[open(\"%1\")]", "shlexec", "dde", NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
1130     {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, TRUE, "[open(\"%s\")]", 0x0, 33},
1131
1132     /* Test unquoted %1 in command and ddeexec
1133      * (test filename has space) */
1134     {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", 0x0, 33},
1135
1136     /* Test ifexec precedence over ddeexec */
1137     {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]", 0x0, 33},
1138
1139     /* Test default DDE topic */
1140     {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
1141
1142     /* Test default DDE application */
1143     {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]", 0x0, 33},
1144
1145     {NULL, NULL, NULL, NULL, NULL, 0, 0x0, 0}
1146 };
1147
1148 static DWORD ddeInst;
1149 static HSZ hszTopic;
1150 static char ddeExec[MAX_PATH], ddeApplication[MAX_PATH];
1151 static BOOL denyNextConnection;
1152
1153 static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv,
1154                                 HSZ hsz1, HSZ hsz2, HDDEDATA hData,
1155                                 ULONG_PTR dwData1, ULONG_PTR dwData2)
1156 {
1157     DWORD size = 0;
1158
1159     if (winetest_debug > 2)
1160         trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
1161               uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
1162
1163     switch (uType)
1164     {
1165         case XTYP_CONNECT:
1166             if (!DdeCmpStringHandles(hsz1, hszTopic))
1167             {
1168                 if (denyNextConnection)
1169                     denyNextConnection = FALSE;
1170                 else
1171                 {
1172                     size = DdeQueryString(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI);
1173                     assert(size < MAX_PATH);
1174                     return (HDDEDATA)TRUE;
1175                 }
1176             }
1177             return (HDDEDATA)FALSE;
1178
1179         case XTYP_EXECUTE:
1180             size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0L);
1181             assert(size < MAX_PATH);
1182             DdeFreeDataHandle(hData);
1183             return (HDDEDATA)DDE_FACK;
1184
1185         default:
1186             return NULL;
1187     }
1188 }
1189
1190 typedef struct
1191 {
1192     char *filename;
1193     DWORD threadIdParent;
1194 } dde_thread_info_t;
1195
1196 static DWORD CALLBACK ddeThread(LPVOID arg)
1197 {
1198     dde_thread_info_t *info = (dde_thread_info_t *)arg;
1199     assert(info && info->filename);
1200     PostThreadMessage(info->threadIdParent,
1201                       WM_QUIT,
1202                       shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, info->filename, NULL, NULL),
1203                       0L);
1204     ExitThread(0);
1205 }
1206
1207 /* ShellExecute won't successfully send DDE commands to console applications after starting them,
1208  * so we run a DDE server in this application, deny the first connection request to make
1209  * ShellExecute start the application, and then process the next DDE connection in this application
1210  * to see the execute command that is sent. */
1211 static void test_dde(void)
1212 {
1213     char filename[MAX_PATH], defApplication[MAX_PATH];
1214     HSZ hszApplication;
1215     dde_thread_info_t info = { filename, GetCurrentThreadId() };
1216     const dde_tests_t* test;
1217     char params[1024];
1218     DWORD threadId;
1219     MSG msg;
1220     int rc;
1221
1222     ddeInst = 0;
1223     rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
1224                         CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
1225     assert(rc == DMLERR_NO_ERROR);
1226
1227     sprintf(filename, "%s\\test file.sde", tmpdir);
1228
1229     /* Default service is application name minus path and extension */
1230     strcpy(defApplication, strrchr(argv0, '\\')+1);
1231     *strchr(defApplication, '.') = 0;
1232
1233     test = dde_tests;
1234     while (test->command)
1235     {
1236         create_test_association(".sde");
1237         create_test_verb_dde(".sde", "Open", 0, test->command, test->ddeexec,
1238                              test->application, test->topic, test->ifexec);
1239         hszApplication = DdeCreateStringHandleA(ddeInst, test->application ?
1240                                                 test->application : defApplication, CP_WINANSI);
1241         hszTopic = DdeCreateStringHandleA(ddeInst, test->topic ? test->topic : SZDDESYS_TOPIC,
1242                                           CP_WINANSI);
1243         assert(hszApplication && hszTopic);
1244         assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER));
1245         denyNextConnection = TRUE;
1246         ddeExec[0] = 0;
1247
1248         assert(CreateThread(NULL, 0, ddeThread, (LPVOID)&info, 0, &threadId));
1249         while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
1250         rc = msg.wParam > 32 ? 33 : msg.wParam;
1251         if ((test->todo & 0x1)==0)
1252         {
1253             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1254                rc, GetLastError());
1255         }
1256         else todo_wine
1257         {
1258             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1259                rc, GetLastError());
1260         }
1261         if (rc == 33)
1262         {
1263             if ((test->todo & 0x2)==0)
1264             {
1265                 okChildInt("argcA", test->expectedArgs + 3);
1266             }
1267             else todo_wine
1268             {
1269                 okChildInt("argcA", test->expectedArgs + 3);
1270             }
1271             if (test->expectedArgs == 1)
1272             {
1273                 if ((test->todo & 0x4) == 0)
1274                 {
1275                     okChildPath("argvA3", filename);
1276                 }
1277                 else todo_wine
1278                 {
1279                     okChildPath("argvA3", filename);
1280                 }
1281             }
1282             if ((test->todo & 0x8) == 0)
1283             {
1284                 sprintf(params, test->expectedDdeExec, filename);
1285                 ok(StrCmpPath(params, ddeExec) == 0,
1286                    "ddeexec expected '%s', got '%s'\n", params, ddeExec);
1287             }
1288             else todo_wine
1289             {
1290                 sprintf(params, test->expectedDdeExec, filename);
1291                 ok(StrCmpPath(params, ddeExec) == 0,
1292                    "ddeexec expected '%s', got '%s'\n", params, ddeExec);
1293             }
1294         }
1295
1296         assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
1297         assert(DdeFreeStringHandle(ddeInst, hszTopic));
1298         assert(DdeFreeStringHandle(ddeInst, hszApplication));
1299         delete_test_association(".sde");
1300         test++;
1301     }
1302
1303     assert(DdeUninitialize(ddeInst));
1304 }
1305
1306 #define DDE_DEFAULT_APP_VARIANTS 2
1307 typedef struct
1308 {
1309     const char* command;
1310     const char* expectedDdeApplication[DDE_DEFAULT_APP_VARIANTS];
1311     int todo;
1312     int rc[DDE_DEFAULT_APP_VARIANTS];
1313 } dde_default_app_tests_t;
1314
1315 static dde_default_app_tests_t dde_default_app_tests[] =
1316 {
1317     /* Windows XP and 98 handle default DDE app names in different ways.
1318      * The application name we see in the first test determines the pattern
1319      * of application names and return codes we will look for. */
1320
1321     /* Test unquoted existing filename with a space */
1322     {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}},
1323     {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}},
1324
1325     /* Test quoted existing filename with a space */
1326     {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}},
1327     {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}},
1328
1329     /* Test unquoted filename with a space that doesn't exist, but
1330      * test2.exe does */
1331     {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}},
1332     {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}},
1333
1334     /* Test quoted filename with a space that does not exist */
1335     {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}},
1336     {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}},
1337
1338     /* Test filename supplied without the extension */
1339     {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}},
1340     {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}},
1341
1342     /* Test an unquoted nonexistent filename */
1343     {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}},
1344     {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}},
1345
1346     /* Test an application that will be found on the path */
1347     {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}},
1348     {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}},
1349
1350     /* Test an application that will not be found on the path */
1351     {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1352     {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1353
1354     {NULL, {NULL}, 0, {0}}
1355 };
1356
1357 static void test_dde_default_app(void)
1358 {
1359     char filename[MAX_PATH];
1360     HSZ hszApplication;
1361     dde_thread_info_t info = { filename, GetCurrentThreadId() };
1362     const dde_default_app_tests_t* test;
1363     char params[1024];
1364     DWORD threadId;
1365     MSG msg;
1366     int rc, which = 0;
1367
1368     ddeInst = 0;
1369     rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
1370                         CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
1371     assert(rc == DMLERR_NO_ERROR);
1372
1373     sprintf(filename, "%s\\test file.sde", tmpdir);
1374
1375     /* It is strictly not necessary to register an application name here, but wine's
1376      * DdeNameService implementation complains if 0L is passed instead of
1377      * hszApplication with DNS_FILTEROFF */
1378     hszApplication = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
1379     hszTopic = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
1380     assert(hszApplication && hszTopic);
1381     assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER | DNS_FILTEROFF));
1382
1383     test = dde_default_app_tests;
1384     while (test->command)
1385     {
1386         create_test_association(".sde");
1387         sprintf(params, test->command, tmpdir);
1388         create_test_verb_dde(".sde", "Open", 1, params, "[test]", NULL,
1389                              "shlexec", NULL);
1390         denyNextConnection = FALSE;
1391         ddeApplication[0] = 0;
1392
1393         /* No application will be run as we will respond to the first DDE event,
1394          * so don't wait for it */
1395         SetEvent(hEvent);
1396
1397         assert(CreateThread(NULL, 0, ddeThread, (LPVOID)&info, 0, &threadId));
1398         while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
1399         rc = msg.wParam > 32 ? 33 : msg.wParam;
1400
1401         /* First test, find which set of test data we expect to see */
1402         if (test == dde_default_app_tests)
1403         {
1404             int i;
1405             for (i=0; i<DDE_DEFAULT_APP_VARIANTS; i++)
1406             {
1407                 if (!strcmp(ddeApplication, test->expectedDdeApplication[i]))
1408                 {
1409                     which = i;
1410                     break;
1411                 }
1412             }
1413             if (i == DDE_DEFAULT_APP_VARIANTS)
1414                 skip("Default DDE application test does not match any available results, using first expected data set.\n");
1415         }
1416
1417         if ((test->todo & 0x1)==0)
1418         {
1419             ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
1420                rc, GetLastError());
1421         }
1422         else todo_wine
1423         {
1424             ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
1425                rc, GetLastError());
1426         }
1427         if (rc == 33)
1428         {
1429             if ((test->todo & 0x2)==0)
1430             {
1431                 ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
1432                    "Expected application '%s', got '%s'\n",
1433                    test->expectedDdeApplication[which], ddeApplication);
1434             }
1435             else todo_wine
1436             {
1437                 ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
1438                    "Expected application '%s', got '%s'\n",
1439                    test->expectedDdeApplication[which], ddeApplication);
1440             }
1441         }
1442
1443         delete_test_association(".sde");
1444         test++;
1445     }
1446
1447     assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
1448     assert(DdeFreeStringHandle(ddeInst, hszTopic));
1449     assert(DdeFreeStringHandle(ddeInst, hszApplication));
1450     assert(DdeUninitialize(ddeInst));
1451 }
1452
1453 static void init_test(void)
1454 {
1455     HMODULE hdll;
1456     HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
1457     char filename[MAX_PATH];
1458     WCHAR lnkfile[MAX_PATH];
1459     char params[1024];
1460     const char* const * testfile;
1461     lnk_desc_t desc;
1462     DWORD rc;
1463     HRESULT r;
1464
1465     hdll=GetModuleHandleA("shell32.dll");
1466     pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
1467     if (pDllGetVersion)
1468     {
1469         dllver.cbSize=sizeof(dllver);
1470         pDllGetVersion(&dllver);
1471         trace("major=%d minor=%d build=%d platform=%d\n",
1472               dllver.dwMajorVersion, dllver.dwMinorVersion,
1473               dllver.dwBuildNumber, dllver.dwPlatformID);
1474     }
1475     else
1476     {
1477         memset(&dllver, 0, sizeof(dllver));
1478     }
1479
1480     r = CoInitialize(NULL);
1481     ok(SUCCEEDED(r), "CoInitialize failed (0x%08x)\n", r);
1482     if (!SUCCEEDED(r))
1483         exit(1);
1484
1485     rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
1486     assert(rc!=0 && rc<sizeof(argv0));
1487     if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
1488     {
1489         strcat(argv0, ".so");
1490         ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
1491            "unable to find argv0!\n");
1492     }
1493
1494     GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
1495     assert(GetTempFileNameA(tmpdir, "wt", 0, child_file)!=0);
1496     init_event(child_file);
1497
1498     /* Set up the test files */
1499     testfile=testfiles;
1500     while (*testfile)
1501     {
1502         HANDLE hfile;
1503
1504         sprintf(filename, *testfile, tmpdir);
1505         hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1506                      FILE_ATTRIBUTE_NORMAL, NULL);
1507         if (hfile==INVALID_HANDLE_VALUE)
1508         {
1509             trace("unable to create '%s': err=%d\n", filename, GetLastError());
1510             assert(0);
1511         }
1512         CloseHandle(hfile);
1513         testfile++;
1514     }
1515
1516     /* Setup the test shortcuts */
1517     sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
1518     MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
1519     desc.description=NULL;
1520     desc.workdir=NULL;
1521     sprintf(filename, "%s\\test file.shlexec", tmpdir);
1522     desc.path=filename;
1523     desc.pidl=NULL;
1524     desc.arguments="ignored";
1525     desc.showcmd=0;
1526     desc.icon=NULL;
1527     desc.icon_id=0;
1528     desc.hotkey=0;
1529     create_lnk(lnkfile, &desc, 0);
1530
1531     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
1532     MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
1533     desc.description=NULL;
1534     desc.workdir=NULL;
1535     desc.path=argv0;
1536     desc.pidl=NULL;
1537     sprintf(params, "shlexec \"%s\" Lnk", child_file);
1538     desc.arguments=params;
1539     desc.showcmd=0;
1540     desc.icon=NULL;
1541     desc.icon_id=0;
1542     desc.hotkey=0;
1543     create_lnk(lnkfile, &desc, 0);
1544
1545     /* Create a basic association suitable for most tests */
1546     create_test_association(".shlexec");
1547     create_test_verb(".shlexec", "Open", 0, "Open \"%1\"");
1548     create_test_verb(".shlexec", "NoQuotes", 0, "NoQuotes %1");
1549     create_test_verb(".shlexec", "LowerL", 0, "LowerL %l");
1550     create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
1551     create_test_verb(".shlexec", "UpperL", 0, "UpperL %L");
1552     create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
1553 }
1554
1555 static void cleanup_test(void)
1556 {
1557     char filename[MAX_PATH];
1558     const char* const * testfile;
1559
1560     /* Delete the test files */
1561     testfile=testfiles;
1562     while (*testfile)
1563     {
1564         sprintf(filename, *testfile, tmpdir);
1565         DeleteFile(filename);
1566         testfile++;
1567     }
1568     DeleteFile(child_file);
1569
1570     /* Delete the test association */
1571     delete_test_association(".shlexec");
1572
1573     CloseHandle(hEvent);
1574
1575     CoUninitialize();
1576 }
1577
1578 START_TEST(shlexec)
1579 {
1580
1581     myARGC = winetest_get_mainargs(&myARGV);
1582     if (myARGC >= 3)
1583     {
1584         doChild(myARGC, myARGV);
1585         exit(0);
1586     }
1587
1588     init_test();
1589
1590     test_filename();
1591     test_find_executable();
1592     test_lnks();
1593     test_exes();
1594     test_exes_long();
1595     test_dde();
1596     test_dde_default_app();
1597
1598     cleanup_test();
1599 }