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