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