shell32/tests: Fix typos.
[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 /* Needed to get SEE_MASK_NOZONECHECKS with the PSDK */
36 #define NTDDI_WINXPSP1 0x05010100
37 #define NTDDI_VERSION NTDDI_WINXPSP1
38 #define _WIN32_WINNT 0x0501
39
40 #include <stdio.h>
41 #include <assert.h>
42
43 #include "wtypes.h"
44 #include "winbase.h"
45 #include "windef.h"
46 #include "shellapi.h"
47 #include "shlwapi.h"
48 #include "wine/test.h"
49
50 #include "shell32_test.h"
51
52
53 static char argv0[MAX_PATH];
54 static int myARGC;
55 static char** myARGV;
56 static char tmpdir[MAX_PATH];
57 static char child_file[MAX_PATH];
58 static DLLVERSIONINFO dllver;
59 static BOOL skip_noassoc_tests = FALSE;
60 static HANDLE dde_ready_event;
61
62
63 /***
64  *
65  * ShellExecute wrappers
66  *
67  ***/
68 static void dump_child(void);
69
70 static HANDLE hEvent;
71 static void init_event(const char* child_file)
72 {
73     char* event_name;
74     event_name=strrchr(child_file, '\\')+1;
75     hEvent=CreateEvent(NULL, FALSE, FALSE, event_name);
76 }
77
78 static void strcat_param(char* str, const char* param)
79 {
80     if (param!=NULL)
81     {
82         strcat(str, "\"");
83         strcat(str, param);
84         strcat(str, "\"");
85     }
86     else
87     {
88         strcat(str, "null");
89     }
90 }
91
92 static char shell_call[2048]="";
93 static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
94 {
95     INT_PTR rc;
96
97     strcpy(shell_call, "ShellExecute(");
98     strcat_param(shell_call, operation);
99     strcat(shell_call, ", ");
100     strcat_param(shell_call, file);
101     strcat(shell_call, ", ");
102     strcat_param(shell_call, parameters);
103     strcat(shell_call, ", ");
104     strcat_param(shell_call, directory);
105     strcat(shell_call, ")");
106     if (winetest_debug > 1)
107         trace("%s\n", shell_call);
108
109     DeleteFile(child_file);
110     SetLastError(0xcafebabe);
111
112     /* FIXME: We cannot use ShellExecuteEx() here because if there is no
113      * association it displays the 'Open With' dialog and I could not find
114      * a flag to prevent this.
115      */
116     rc=(INT_PTR)ShellExecute(NULL, operation, file, parameters, directory, SW_SHOWNORMAL);
117
118     if (rc > 32)
119     {
120         int wait_rc;
121         wait_rc=WaitForSingleObject(hEvent, 5000);
122         if (wait_rc == WAIT_TIMEOUT)
123         {
124             HWND wnd = FindWindowA("#32770", "Windows");
125             if (wnd != NULL)
126             {
127                 SendMessage(wnd, WM_CLOSE, 0, 0);
128                 win_skip("Skipping shellexecute of file with unassociated extension\n");
129                 skip_noassoc_tests = TRUE;
130                 rc = SE_ERR_NOASSOC;
131             }
132         }
133         ok(wait_rc==WAIT_OBJECT_0 || rc <= 32, "WaitForSingleObject returned %d\n", wait_rc);
134     }
135     /* The child process may have changed the result file, so let profile
136      * functions know about it
137      */
138     WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
139     if (rc > 32)
140         dump_child();
141
142     return rc;
143 }
144
145 static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
146                             LPCSTR parameters, LPCSTR directory)
147 {
148     SHELLEXECUTEINFO sei;
149     BOOL success;
150     INT_PTR rc;
151
152     strcpy(shell_call, "ShellExecuteEx(");
153     strcat_param(shell_call, operation);
154     strcat(shell_call, ", ");
155     strcat_param(shell_call, file);
156     strcat(shell_call, ", ");
157     strcat_param(shell_call, parameters);
158     strcat(shell_call, ", ");
159     strcat_param(shell_call, directory);
160     strcat(shell_call, ")");
161     if (winetest_debug > 1)
162         trace("%s\n", shell_call);
163
164     sei.cbSize=sizeof(sei);
165     sei.fMask=SEE_MASK_NOCLOSEPROCESS | mask;
166     sei.hwnd=NULL;
167     sei.lpVerb=operation;
168     sei.lpFile=file;
169     sei.lpParameters=parameters;
170     sei.lpDirectory=directory;
171     sei.nShow=SW_SHOWNORMAL;
172     sei.hInstApp=NULL; /* Out */
173     sei.lpIDList=NULL;
174     sei.lpClass=NULL;
175     sei.hkeyClass=NULL;
176     sei.dwHotKey=0;
177     U(sei).hIcon=NULL;
178     sei.hProcess=NULL; /* Out */
179
180     DeleteFile(child_file);
181     SetLastError(0xcafebabe);
182     success=ShellExecuteEx(&sei);
183     rc=(INT_PTR)sei.hInstApp;
184     ok((success && rc > 32) || (!success && rc <= 32),
185        "%s rc=%d and hInstApp=%ld is not allowed\n", shell_call, success, rc);
186
187     if (rc > 32)
188     {
189         int wait_rc;
190         if (sei.hProcess!=NULL)
191         {
192             wait_rc=WaitForSingleObject(sei.hProcess, 5000);
193             ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject(hProcess) returned %d\n", wait_rc);
194         }
195         wait_rc=WaitForSingleObject(hEvent, 5000);
196         ok(wait_rc==WAIT_OBJECT_0, "WaitForSingleObject returned %d\n", wait_rc);
197     }
198     /* The child process may have changed the result file, so let profile
199      * functions know about it
200      */
201     WritePrivateProfileStringA(NULL, NULL, NULL, child_file);
202     if (rc > 32)
203         dump_child();
204
205     return rc;
206 }
207
208
209
210 /***
211  *
212  * Functions to create / delete associations wrappers
213  *
214  ***/
215
216 static BOOL create_test_association(const char* extension)
217 {
218     HKEY hkey, hkey_shell;
219     char class[MAX_PATH];
220     LONG rc;
221
222     sprintf(class, "shlexec%s", extension);
223     rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
224                       NULL, &hkey, NULL);
225     if (rc != ERROR_SUCCESS)
226         return FALSE;
227
228     rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, (LPBYTE) class, strlen(class)+1);
229     ok(rc==ERROR_SUCCESS, "RegSetValueEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc);
230     CloseHandle(hkey);
231
232     rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
233                       KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
234     ok(rc==ERROR_SUCCESS, "RegCreateKeyEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc);
235
236     rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
237                       KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
238     ok(rc==ERROR_SUCCESS, "RegCreateKeyEx 'shell' failed, expected ERROR_SUCCESS, got %d\n", rc);
239
240     CloseHandle(hkey);
241     CloseHandle(hkey_shell);
242
243     return TRUE;
244 }
245
246 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
247 static LSTATUS myRegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
248 {
249     LONG ret;
250     DWORD dwMaxSubkeyLen, dwMaxValueLen;
251     DWORD dwMaxLen, dwSize;
252     CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
253     HKEY hSubKey = hKey;
254
255     if(lpszSubKey)
256     {
257         ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
258         if (ret) return ret;
259     }
260
261     /* Get highest length for keys, values */
262     ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
263             &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
264     if (ret) goto cleanup;
265
266     dwMaxSubkeyLen++;
267     dwMaxValueLen++;
268     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
269     if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
270     {
271         /* Name too big: alloc a buffer for it */
272         if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
273         {
274             ret = ERROR_NOT_ENOUGH_MEMORY;
275             goto cleanup;
276         }
277     }
278
279
280     /* Recursively delete all the subkeys */
281     while (TRUE)
282     {
283         dwSize = dwMaxLen;
284         if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
285                           NULL, NULL, NULL)) break;
286
287         ret = myRegDeleteTreeA(hSubKey, lpszName);
288         if (ret) goto cleanup;
289     }
290
291     if (lpszSubKey)
292         ret = RegDeleteKeyA(hKey, lpszSubKey);
293     else
294         while (TRUE)
295         {
296             dwSize = dwMaxLen;
297             if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
298                   NULL, NULL, NULL, NULL)) break;
299
300             ret = RegDeleteValueA(hKey, lpszName);
301             if (ret) goto cleanup;
302         }
303
304 cleanup:
305     /* Free buffer if allocated */
306     if (lpszName != szNameBuf)
307         HeapFree( GetProcessHeap(), 0, lpszName);
308     if(lpszSubKey)
309         RegCloseKey(hSubKey);
310     return ret;
311 }
312
313 static void delete_test_association(const char* extension)
314 {
315     char class[MAX_PATH];
316
317     sprintf(class, "shlexec%s", extension);
318     myRegDeleteTreeA(HKEY_CLASSES_ROOT, class);
319     myRegDeleteTreeA(HKEY_CLASSES_ROOT, extension);
320 }
321
322 static void create_test_verb_dde(const char* extension, const char* verb,
323                                  int rawcmd, const char* cmdtail, const char *ddeexec,
324                                  const char *application, const char *topic,
325                                  const char *ifexec)
326 {
327     HKEY hkey_shell, hkey_verb, hkey_cmd;
328     char shell[MAX_PATH];
329     char* cmd;
330     LONG rc;
331
332     sprintf(shell, "shlexec%s\\shell", extension);
333     rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
334                     KEY_CREATE_SUB_KEY, &hkey_shell);
335     assert(rc==ERROR_SUCCESS);
336     rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
337                       NULL, &hkey_verb, NULL);
338     assert(rc==ERROR_SUCCESS);
339     rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
340                       NULL, &hkey_cmd, NULL);
341     assert(rc==ERROR_SUCCESS);
342
343     if (rawcmd)
344     {
345         rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmdtail, strlen(cmdtail)+1);
346     }
347     else
348     {
349         cmd=HeapAlloc(GetProcessHeap(), 0, strlen(argv0)+10+strlen(child_file)+2+strlen(cmdtail)+1);
350         sprintf(cmd,"%s shlexec \"%s\" %s", argv0, child_file, cmdtail);
351         rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmd, strlen(cmd)+1);
352         assert(rc==ERROR_SUCCESS);
353         HeapFree(GetProcessHeap(), 0, cmd);
354     }
355
356     if (ddeexec)
357     {
358         HKEY hkey_ddeexec, hkey_application, hkey_topic, hkey_ifexec;
359
360         rc=RegCreateKeyEx(hkey_verb, "ddeexec", 0, NULL, 0, KEY_SET_VALUE |
361                           KEY_CREATE_SUB_KEY, NULL, &hkey_ddeexec, NULL);
362         assert(rc==ERROR_SUCCESS);
363         rc=RegSetValueEx(hkey_ddeexec, NULL, 0, REG_SZ, (LPBYTE)ddeexec,
364                          strlen(ddeexec)+1);
365         assert(rc==ERROR_SUCCESS);
366         if (application)
367         {
368             rc=RegCreateKeyEx(hkey_ddeexec, "application", 0, NULL, 0, KEY_SET_VALUE,
369                               NULL, &hkey_application, NULL);
370             assert(rc==ERROR_SUCCESS);
371             rc=RegSetValueEx(hkey_application, NULL, 0, REG_SZ, (LPBYTE)application,
372                              strlen(application)+1);
373             assert(rc==ERROR_SUCCESS);
374             CloseHandle(hkey_application);
375         }
376         if (topic)
377         {
378             rc=RegCreateKeyEx(hkey_ddeexec, "topic", 0, NULL, 0, KEY_SET_VALUE,
379                               NULL, &hkey_topic, NULL);
380             assert(rc==ERROR_SUCCESS);
381             rc=RegSetValueEx(hkey_topic, NULL, 0, REG_SZ, (LPBYTE)topic,
382                              strlen(topic)+1);
383             assert(rc==ERROR_SUCCESS);
384             CloseHandle(hkey_topic);
385         }
386         if (ifexec)
387         {
388             rc=RegCreateKeyEx(hkey_ddeexec, "ifexec", 0, NULL, 0, KEY_SET_VALUE,
389                               NULL, &hkey_ifexec, NULL);
390             assert(rc==ERROR_SUCCESS);
391             rc=RegSetValueEx(hkey_ifexec, NULL, 0, REG_SZ, (LPBYTE)ifexec,
392                              strlen(ifexec)+1);
393             assert(rc==ERROR_SUCCESS);
394             CloseHandle(hkey_ifexec);
395         }
396         CloseHandle(hkey_ddeexec);
397     }
398
399     CloseHandle(hkey_shell);
400     CloseHandle(hkey_verb);
401     CloseHandle(hkey_cmd);
402 }
403
404 static void create_test_verb(const char* extension, const char* verb,
405                              int rawcmd, const char* cmdtail)
406 {
407     create_test_verb_dde(extension, verb, rawcmd, cmdtail, NULL, NULL,
408                          NULL, NULL);
409 }
410
411 /***
412  *
413  * Functions to check that the child process was started just right
414  * (borrowed from dlls/kernel32/tests/process.c)
415  *
416  ***/
417
418 static const char* encodeA(const char* str)
419 {
420     static char encoded[2*1024+1];
421     char*       ptr;
422     size_t      len,i;
423
424     if (!str) return "";
425     len = strlen(str) + 1;
426     if (len >= sizeof(encoded)/2)
427     {
428         fprintf(stderr, "string is too long!\n");
429         assert(0);
430     }
431     ptr = encoded;
432     for (i = 0; i < len; i++)
433         sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
434     ptr[2 * len] = '\0';
435     return ptr;
436 }
437
438 static unsigned decode_char(char c)
439 {
440     if (c >= '0' && c <= '9') return c - '0';
441     if (c >= 'a' && c <= 'f') return c - 'a' + 10;
442     assert(c >= 'A' && c <= 'F');
443     return c - 'A' + 10;
444 }
445
446 static char* decodeA(const char* str)
447 {
448     static char decoded[1024];
449     char*       ptr;
450     size_t      len,i;
451
452     len = strlen(str) / 2;
453     if (!len--) return NULL;
454     if (len >= sizeof(decoded))
455     {
456         fprintf(stderr, "string is too long!\n");
457         assert(0);
458     }
459     ptr = decoded;
460     for (i = 0; i < len; i++)
461         ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
462     ptr[len] = '\0';
463     return ptr;
464 }
465
466 static void     childPrintf(HANDLE h, const char* fmt, ...)
467 {
468     va_list     valist;
469     char        buffer[1024];
470     DWORD       w;
471
472     va_start(valist, fmt);
473     vsprintf(buffer, fmt, valist);
474     va_end(valist);
475     WriteFile(h, buffer, strlen(buffer), &w, NULL);
476 }
477
478 static DWORD ddeInst;
479 static HSZ hszTopic;
480 static char ddeExec[MAX_PATH], ddeApplication[MAX_PATH];
481 static BOOL post_quit_on_execute;
482
483 static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv,
484                                HSZ hsz1, HSZ hsz2, HDDEDATA hData,
485                                ULONG_PTR dwData1, ULONG_PTR dwData2)
486 {
487     DWORD size = 0;
488
489     if (winetest_debug > 2)
490         trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
491               uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
492
493     switch (uType)
494     {
495         case XTYP_CONNECT:
496             if (!DdeCmpStringHandles(hsz1, hszTopic))
497             {
498                 size = DdeQueryString(ddeInst, hsz2, ddeApplication, MAX_PATH, CP_WINANSI);
499                 assert(size < MAX_PATH);
500                 return (HDDEDATA)TRUE;
501             }
502             return (HDDEDATA)FALSE;
503
504         case XTYP_EXECUTE:
505             size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0L);
506             assert(size < MAX_PATH);
507             DdeFreeDataHandle(hData);
508             if (post_quit_on_execute)
509                 PostQuitMessage(0);
510             return (HDDEDATA)DDE_FACK;
511
512         default:
513             return NULL;
514     }
515 }
516
517 /*
518  * This is just to make sure the child won't run forever stuck in a GetMessage()
519  * loop when DDE fails for some reason.
520  */
521 static void CALLBACK childTimeout(HWND wnd, UINT msg, UINT_PTR timer, DWORD time)
522 {
523     trace("childTimeout called\n");
524
525     PostQuitMessage(0);
526 }
527
528 static void doChild(int argc, char** argv)
529 {
530     char *filename, longpath[MAX_PATH] = "";
531     HANDLE hFile, map;
532     int i;
533     int rc;
534     HSZ hszApplication;
535     UINT_PTR timer;
536     HANDLE dde_ready;
537     MSG msg;
538     char *shared_block;
539
540     filename=argv[2];
541     hFile=CreateFileA(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
542     if (hFile == INVALID_HANDLE_VALUE)
543         return;
544
545     /* Arguments */
546     childPrintf(hFile, "[Arguments]\r\n");
547     if (winetest_debug > 2)
548         trace("argcA=%d\n", argc);
549     childPrintf(hFile, "argcA=%d\r\n", argc);
550     for (i = 0; i < argc; i++)
551     {
552         if (winetest_debug > 2)
553             trace("argvA%d=%s\n", i, argv[i]);
554         childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i]));
555     }
556     GetModuleFileNameA(GetModuleHandleA(NULL), longpath, MAX_PATH);
557     childPrintf(hFile, "longPath=%s\r\n", encodeA(longpath));
558
559     map = OpenFileMappingA(FILE_MAP_READ, FALSE, "winetest_shlexec_dde_map");
560     if (map != NULL)
561     {
562         shared_block = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 4096);
563         CloseHandle(map);
564         if (shared_block[0] != '\0' || shared_block[1] != '\0')
565         {
566             post_quit_on_execute = TRUE;
567             ddeInst = 0;
568             rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
569                                 CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
570             assert(rc == DMLERR_NO_ERROR);
571             hszApplication = DdeCreateStringHandleA(ddeInst, shared_block, CP_WINANSI);
572             hszTopic = DdeCreateStringHandleA(ddeInst, shared_block + strlen(shared_block) + 1, CP_WINANSI);
573             assert(hszApplication && hszTopic);
574             assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER | DNS_FILTEROFF));
575
576             timer = SetTimer(NULL, 0, 2500, childTimeout);
577
578             dde_ready = OpenEvent(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready");
579             SetEvent(dde_ready);
580             CloseHandle(dde_ready);
581
582             while (GetMessage(&msg, NULL, 0, 0))
583                 DispatchMessage(&msg);
584
585             Sleep(500);
586             KillTimer(NULL, timer);
587             assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
588             assert(DdeFreeStringHandle(ddeInst, hszTopic));
589             assert(DdeFreeStringHandle(ddeInst, hszApplication));
590             assert(DdeUninitialize(ddeInst));
591         }
592         else
593         {
594             dde_ready = OpenEvent(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready");
595             SetEvent(dde_ready);
596             CloseHandle(dde_ready);
597         }
598
599         UnmapViewOfFile(shared_block);
600
601         childPrintf(hFile, "ddeExec=%s\r\n", encodeA(ddeExec));
602     }
603
604     CloseHandle(hFile);
605
606     init_event(filename);
607     SetEvent(hEvent);
608     CloseHandle(hEvent);
609 }
610
611 static char* getChildString(const char* sect, const char* key)
612 {
613     char        buf[1024];
614     char*       ret;
615
616     GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file);
617     if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
618     assert(!(strlen(buf) & 1));
619     ret = decodeA(buf);
620     return ret;
621 }
622
623 static void dump_child(void)
624 {
625     if (winetest_debug > 1)
626     {
627         char key[18];
628         char* str;
629         int i, c;
630
631         c=GetPrivateProfileIntA("Arguments", "argcA", -1, child_file);
632         trace("argcA=%d\n",c);
633         for (i=0;i<c;i++)
634         {
635             sprintf(key, "argvA%d", i);
636             str=getChildString("Arguments", key);
637             trace("%s=%s\n", key, str);
638         }
639     }
640 }
641
642 static int StrCmpPath(const char* s1, const char* s2)
643 {
644     if (!s1 && !s2) return 0;
645     if (!s2) return 1;
646     if (!s1) return -1;
647     while (*s1)
648     {
649         if (!*s2)
650         {
651             if (*s1=='.')
652                 s1++;
653             return (*s1-*s2);
654         }
655         if ((*s1=='/' || *s1=='\\') && (*s2=='/' || *s2=='\\'))
656         {
657             while (*s1=='/' || *s1=='\\')
658                 s1++;
659             while (*s2=='/' || *s2=='\\')
660                 s2++;
661         }
662         else if (toupper(*s1)==toupper(*s2))
663         {
664             s1++;
665             s2++;
666         }
667         else
668         {
669             return (*s1-*s2);
670         }
671     }
672     if (*s2=='.')
673         s2++;
674     if (*s2)
675         return -1;
676     return 0;
677 }
678
679 static void _okChildString(const char* file, int line, const char* key, const char* expected)
680 {
681     char* result;
682     result=getChildString("Arguments", key);
683     if (!result)
684     {
685         ok_(file, line)(FALSE, "%s expected '%s', but key not found or empty\n", key, expected);
686         return;
687     }
688     ok_(file, line)(lstrcmpiA(result, expected) == 0,
689                     "%s expected '%s', got '%s'\n", key, expected, result);
690 }
691
692 static void _okChildPath(const char* file, int line, const char* key, const char* expected)
693 {
694     char* result;
695     result=getChildString("Arguments", key);
696     if (!result)
697     {
698         ok_(file, line)(FALSE, "%s expected '%s', but key not found or empty\n", key, expected);
699         return;
700     }
701     ok_(file, line)(StrCmpPath(result, expected) == 0,
702                     "%s expected '%s', got '%s'\n", key, expected, result);
703 }
704
705 static void _okChildInt(const char* file, int line, const char* key, int expected)
706 {
707     INT result;
708     result=GetPrivateProfileIntA("Arguments", key, expected, child_file);
709     ok_(file, line)(result == expected,
710                     "%s expected %d, but got %d\n", key, expected, result);
711 }
712
713 #define okChildString(key, expected) _okChildString(__FILE__, __LINE__, (key), (expected))
714 #define okChildPath(key, expected) _okChildPath(__FILE__, __LINE__, (key), (expected))
715 #define okChildInt(key, expected)    _okChildInt(__FILE__, __LINE__, (key), (expected))
716
717 /***
718  *
719  * GetLongPathNameA equivalent that supports Win95 and WinNT
720  *
721  ***/
722
723 static DWORD get_long_path_name(const char* shortpath, char* longpath, DWORD longlen)
724 {
725     char tmplongpath[MAX_PATH];
726     const char* p;
727     DWORD sp = 0, lp = 0;
728     DWORD tmplen;
729     WIN32_FIND_DATAA wfd;
730     HANDLE goit;
731
732     if (!shortpath || !shortpath[0])
733         return 0;
734
735     if (shortpath[1] == ':')
736     {
737         tmplongpath[0] = shortpath[0];
738         tmplongpath[1] = ':';
739         lp = sp = 2;
740     }
741
742     while (shortpath[sp])
743     {
744         /* check for path delimiters and reproduce them */
745         if (shortpath[sp] == '\\' || shortpath[sp] == '/')
746         {
747             if (!lp || tmplongpath[lp-1] != '\\')
748             {
749                 /* strip double "\\" */
750                 tmplongpath[lp++] = '\\';
751             }
752             tmplongpath[lp] = 0; /* terminate string */
753             sp++;
754             continue;
755         }
756
757         p = shortpath + sp;
758         if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
759         {
760             tmplongpath[lp++] = *p++;
761             tmplongpath[lp++] = *p++;
762         }
763         for (; *p && *p != '/' && *p != '\\'; p++);
764         tmplen = p - (shortpath + sp);
765         lstrcpyn(tmplongpath + lp, shortpath + sp, tmplen + 1);
766         /* Check if the file exists and use the existing file name */
767         goit = FindFirstFileA(tmplongpath, &wfd);
768         if (goit == INVALID_HANDLE_VALUE)
769             return 0;
770         FindClose(goit);
771         strcpy(tmplongpath + lp, wfd.cFileName);
772         lp += strlen(tmplongpath + lp);
773         sp += tmplen;
774     }
775     tmplen = strlen(shortpath) - 1;
776     if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
777         (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
778         tmplongpath[lp++] = shortpath[tmplen];
779     tmplongpath[lp] = 0;
780
781     tmplen = strlen(tmplongpath) + 1;
782     if (tmplen <= longlen)
783     {
784         strcpy(longpath, tmplongpath);
785         tmplen--; /* length without 0 */
786     }
787
788     return tmplen;
789 }
790
791 /***
792  *
793  * PathFindFileNameA equivalent that supports WinNT
794  *
795  ***/
796
797 static LPSTR path_find_file_name(LPCSTR lpszPath)
798 {
799   LPCSTR lastSlash = lpszPath;
800
801   while (lpszPath && *lpszPath)
802   {
803     if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') &&
804         lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/')
805       lastSlash = lpszPath + 1;
806     lpszPath = CharNext(lpszPath);
807   }
808   return (LPSTR)lastSlash;
809 }
810
811 /***
812  *
813  * Tests
814  *
815  ***/
816
817 static const char* testfiles[]=
818 {
819     "%s\\test file.shlexec",
820     "%s\\%%nasty%% $file.shlexec",
821     "%s\\test file.noassoc",
822     "%s\\test file.noassoc.shlexec",
823     "%s\\test file.shlexec.noassoc",
824     "%s\\test_shortcut_shlexec.lnk",
825     "%s\\test_shortcut_exe.lnk",
826     "%s\\test file.shl",
827     "%s\\test file.shlfoo",
828     "%s\\test file.sfe",
829     "%s\\masked file.shlexec",
830     "%s\\masked",
831     "%s\\test file.sde",
832     "%s\\test file.exe",
833     "%s\\test2.exe",
834     "%s\\simple.shlexec",
835     "%s\\drawback_file.noassoc",
836     "%s\\drawback_file.noassoc foo.shlexec",
837     "%s\\drawback_nonexist.noassoc foo.shlexec",
838     NULL
839 };
840
841 typedef struct
842 {
843     const char* verb;
844     const char* basename;
845     int todo;
846     int rc;
847 } filename_tests_t;
848
849 static filename_tests_t filename_tests[]=
850 {
851     /* Test bad / nonexistent filenames */
852     {NULL,           "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF},
853     {NULL,           "%s\\nonexistent.noassoc", 0x0, SE_ERR_FNF},
854
855     /* Standard tests */
856     {NULL,           "%s\\test file.shlexec",   0x0, 33},
857     {NULL,           "%s\\test file.shlexec.",  0x0, 33},
858     {NULL,           "%s\\%%nasty%% $file.shlexec", 0x0, 33},
859     {NULL,           "%s/test file.shlexec",    0x0, 33},
860
861     /* Test filenames with no association */
862     {NULL,           "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
863
864     /* Test double extensions */
865     {NULL,           "%s\\test file.noassoc.shlexec", 0x0, 33},
866     {NULL,           "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC},
867
868     /* Test alternate verbs */
869     {"LowerL",       "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF},
870     {"LowerL",       "%s\\test file.noassoc",   0x0,  SE_ERR_NOASSOC},
871
872     {"QuotedLowerL", "%s\\test file.shlexec",   0x0, 33},
873     {"QuotedUpperL", "%s\\test file.shlexec",   0x0, 33},
874
875     /* Test file masked due to space */
876     {NULL,           "%s\\masked file.shlexec",   0x1, 33},
877     /* Test if quoting prevents the masking */
878     {NULL,           "%s\\masked file.shlexec",   0x40, 33},
879
880     {NULL, NULL, 0}
881 };
882
883 static filename_tests_t noquotes_tests[]=
884 {
885     /* Test unquoted '%1' thingies */
886     {"NoQuotes",     "%s\\test file.shlexec",   0xa, 33},
887     {"LowerL",       "%s\\test file.shlexec",   0xa, 33},
888     {"UpperL",       "%s\\test file.shlexec",   0xa, 33},
889
890     {NULL, NULL, 0}
891 };
892
893 static void test_lpFile_parsed(void)
894 {
895     /* basename tmpdir */
896     const char* shorttmpdir;
897
898     const char *testfile;
899     char fileA[MAX_PATH];
900
901     int rc;
902
903     GetTempPathA(sizeof(fileA), fileA);
904     shorttmpdir = tmpdir + strlen(fileA);
905
906     /* ensure tmpdir is in %TEMP%: GetTempPath() can succeed even if TEMP is undefined */
907     SetEnvironmentVariableA("TEMP", fileA);
908
909     /* existing "drawback_file.noassoc" prevents finding "drawback_file.noassoc foo.shlexec" on wine */
910     testfile = "%s\\drawback_file.noassoc foo.shlexec";
911     sprintf(fileA, testfile, tmpdir);
912     rc=shell_execute(NULL, fileA, NULL, NULL);
913     todo_wine {
914         ok(rc>32,
915             "expected success (33), got %s (%d), lpFile: %s\n",
916             rc > 32 ? "success" : "failure", rc, fileA
917             );
918     }
919
920     /* if quoted, existing "drawback_file.noassoc" not prevents finding "drawback_file.noassoc foo.shlexec" on wine */
921     testfile = "\"%s\\drawback_file.noassoc foo.shlexec\"";
922     sprintf(fileA, testfile, tmpdir);
923     rc=shell_execute(NULL, fileA, NULL, NULL);
924     ok(rc>32 || broken(rc == 2) /* Win95/NT4 */,
925         "expected success (33), got %s (%d), lpFile: %s\n",
926         rc > 32 ? "success" : "failure", rc, fileA
927         );
928
929     /* error should be 2, not 31 */
930     testfile = "\"%s\\drawback_file.noassoc\" foo.shlexec";
931     sprintf(fileA, testfile, tmpdir);
932     rc=shell_execute(NULL, fileA, NULL, NULL);
933     ok(rc==2,
934         "expected failure (2), got %s (%d), lpFile: %s\n",
935         rc > 32 ? "success" : "failure", rc, fileA
936         );
937
938     /* ""command"" not works on wine (and real win9x and w2k) */
939     testfile = "\"\"%s\\simple.shlexec\"\"";
940     sprintf(fileA, testfile, tmpdir);
941     rc=shell_execute(NULL, fileA, NULL, NULL);
942     todo_wine {
943         ok(rc>32 || broken(rc == 2) /* Win9x/2000 */,
944             "expected success (33), got %s (%d), lpFile: %s\n",
945             rc > 32 ? "success" : "failure", rc, fileA
946             );
947     }
948
949     /* nonexisting "drawback_nonexist.noassoc" not prevents finding "drawback_nonexist.noassoc foo.shlexec" on wine */
950     testfile = "%s\\drawback_nonexist.noassoc foo.shlexec";
951     sprintf(fileA, testfile, tmpdir);
952     rc=shell_execute(NULL, fileA, NULL, NULL);
953     ok(rc>32,
954         "expected success (33), got %s (%d), lpFile: %s\n",
955         rc > 32 ? "success" : "failure", rc, fileA
956         );
957
958     /* is SEE_MASK_DOENVSUBST default flag? Should only be when XP emulates 9x (XP bug or real 95 or ME behavior ?) */
959     testfile = "%%TEMP%%\\%s\\simple.shlexec";
960     sprintf(fileA, testfile, shorttmpdir);
961     rc=shell_execute(NULL, fileA, NULL, NULL);
962     todo_wine {
963         ok(rc==2,
964             "expected failure (2), got %s (%d), lpFile: %s\n",
965             rc > 32 ? "success" : "failure", rc, fileA
966             );
967     }
968
969     /* quoted */
970     testfile = "\"%%TEMP%%\\%s\\simple.shlexec\"";
971     sprintf(fileA, testfile, shorttmpdir);
972     rc=shell_execute(NULL, fileA, NULL, NULL);
973     todo_wine {
974         ok(rc==2,
975             "expected failure (2), got %s (%d), lpFile: %s\n",
976             rc > 32 ? "success" : "failure", rc, fileA
977             );
978     }
979
980     /* test SEE_MASK_DOENVSUBST works */
981     testfile = "%%TEMP%%\\%s\\simple.shlexec";
982     sprintf(fileA, testfile, shorttmpdir);
983     rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, fileA, NULL, NULL);
984     ok(rc>32,
985         "expected success (33), got %s (%d), lpFile: %s\n",
986         rc > 32 ? "success" : "failure", rc, fileA
987         );
988
989     /* quoted lpFile not works only on real win95 and nt4 */
990     testfile = "\"%%TEMP%%\\%s\\simple.shlexec\"";
991     sprintf(fileA, testfile, shorttmpdir);
992     rc=shell_execute_ex(SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI, NULL, fileA, NULL, NULL);
993     ok(rc>32 || broken(rc == 2) /* Win95/NT4 */,
994         "expected success (33), got %s (%d), lpFile: %s\n",
995         rc > 32 ? "success" : "failure", rc, fileA
996         );
997
998 }
999
1000 static void test_argify(void)
1001 {
1002     char fileA[MAX_PATH];
1003
1004     int rc;
1005
1006     sprintf(fileA, "%s\\test file.shlexec", tmpdir);
1007
1008     /* %2 */
1009     rc=shell_execute("NoQuotesParam2", fileA, "a b", NULL);
1010     ok(rc>32,
1011         "expected success (33), got %s (%d), lpFile: %s\n",
1012         rc > 32 ? "success" : "failure", rc, fileA
1013         );
1014     if (rc>32)
1015     {
1016         okChildInt("argcA", 5);
1017         okChildString("argvA4", "a");
1018     }
1019
1020     /* %2 */
1021     /* '"a"""'   -> 'a"' */
1022     rc=shell_execute("NoQuotesParam2", fileA, "\"a:\"\"some string\"\"\"", NULL);
1023     ok(rc>32,
1024         "expected success (33), got %s (%d), lpFile: %s\n",
1025         rc > 32 ? "success" : "failure", rc, fileA
1026         );
1027     if (rc>32)
1028     {
1029         okChildInt("argcA", 5);
1030         todo_wine {
1031             okChildString("argvA4", "a:some string");
1032         }
1033     }
1034
1035     /* %2 */
1036     /* backslash isn't escape char
1037      * '"a\""'   -> '"a\""' */
1038     rc=shell_execute("NoQuotesParam2", fileA, "\"a:\\\"some string\\\"\"", NULL);
1039     ok(rc>32,
1040         "expected success (33), got %s (%d), lpFile: %s\n",
1041         rc > 32 ? "success" : "failure", rc, fileA
1042         );
1043     if (rc>32)
1044     {
1045         okChildInt("argcA", 5);
1046         todo_wine {
1047             okChildString("argvA4", "a:\\");
1048         }
1049     }
1050
1051     /* "%2" */
1052     /* \t isn't whitespace */
1053     rc=shell_execute("QuotedParam2", fileA, "a\tb c", NULL);
1054     ok(rc>32,
1055         "expected success (33), got %s (%d), lpFile: %s\n",
1056         rc > 32 ? "success" : "failure", rc, fileA
1057         );
1058     if (rc>32)
1059     {
1060         okChildInt("argcA", 5);
1061         todo_wine {
1062             okChildString("argvA4", "a\tb");
1063         }
1064     }
1065
1066     /* %* */
1067     rc=shell_execute("NoQuotesAllParams", fileA, "a b c d e f g h", NULL);
1068     ok(rc>32,
1069         "expected success (33), got %s (%d), lpFile: %s\n",
1070         rc > 32 ? "success" : "failure", rc, fileA
1071         );
1072     if (rc>32)
1073     {
1074         todo_wine {
1075             okChildInt("argcA", 12);
1076             okChildString("argvA4", "a");
1077             okChildString("argvA11", "h");
1078         }
1079     }
1080
1081     /* %* can sometimes contain only whitespaces and no args */
1082     rc=shell_execute("QuotedAllParams", fileA, "   ", NULL);
1083     ok(rc>32,
1084         "expected success (33), got %s (%d), lpFile: %s\n",
1085         rc > 32 ? "success" : "failure", rc, fileA
1086         );
1087     if (rc>32)
1088     {
1089         todo_wine {
1090             okChildInt("argcA", 5);
1091             okChildString("argvA4", "   ");
1092         }
1093     }
1094
1095     /* %~3 */
1096     rc=shell_execute("NoQuotesParams345etc", fileA, "a b c d e f g h", NULL);
1097     ok(rc>32,
1098         "expected success (33), got %s (%d), lpFile: %s\n",
1099         rc > 32 ? "success" : "failure", rc, fileA
1100         );
1101     if (rc>32)
1102     {
1103         todo_wine {
1104             okChildInt("argcA", 11);
1105             okChildString("argvA4", "b");
1106             okChildString("argvA10", "h");
1107         }
1108     }
1109
1110     /* %~3 is rest of command line starting with whitespaces after 2nd arg */
1111     rc=shell_execute("QuotedParams345etc", fileA, "a    ", NULL);
1112     ok(rc>32,
1113         "expected success (33), got %s (%d), lpFile: %s\n",
1114         rc > 32 ? "success" : "failure", rc, fileA
1115         );
1116     if (rc>32)
1117     {
1118         okChildInt("argcA", 5);
1119         todo_wine {
1120             okChildString("argvA4", "    ");
1121         }
1122     }
1123
1124 }
1125
1126 static void test_filename(void)
1127 {
1128     char filename[MAX_PATH];
1129     const filename_tests_t* test;
1130     char* c;
1131     int rc;
1132
1133     test=filename_tests;
1134     while (test->basename)
1135     {
1136         BOOL quotedfile = FALSE;
1137
1138         if (skip_noassoc_tests && test->rc == SE_ERR_NOASSOC)
1139         {
1140             win_skip("Skipping shellexecute of file with unassociated extension\n");
1141             test++;
1142             continue;
1143         }
1144
1145         sprintf(filename, test->basename, tmpdir);
1146         if (strchr(filename, '/'))
1147         {
1148             c=filename;
1149             while (*c)
1150             {
1151                 if (*c=='\\')
1152                     *c='/';
1153                 c++;
1154             }
1155         }
1156         if ((test->todo & 0x40)==0)
1157         {
1158             rc=shell_execute(test->verb, filename, NULL, NULL);
1159         }
1160         else
1161         {
1162             char quoted[MAX_PATH + 2];
1163
1164             quotedfile = TRUE;
1165             sprintf(quoted, "\"%s\"", filename);
1166             rc=shell_execute(test->verb, quoted, NULL, NULL);
1167         }
1168         if (rc > 32)
1169             rc=33;
1170         if ((test->todo & 0x1)==0)
1171         {
1172             ok(rc==test->rc ||
1173                broken(quotedfile && rc == 2), /* NT4 */
1174                "%s failed: rc=%d err=%d\n", shell_call,
1175                rc, GetLastError());
1176         }
1177         else todo_wine
1178         {
1179             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1180                rc, GetLastError());
1181         }
1182         if (rc == 33)
1183         {
1184             const char* verb;
1185             if ((test->todo & 0x2)==0)
1186             {
1187                 okChildInt("argcA", 5);
1188             }
1189             else todo_wine
1190             {
1191                 okChildInt("argcA", 5);
1192             }
1193             verb=(test->verb ? test->verb : "Open");
1194             if ((test->todo & 0x4)==0)
1195             {
1196                 okChildString("argvA3", verb);
1197             }
1198             else todo_wine
1199             {
1200                 okChildString("argvA3", verb);
1201             }
1202             if ((test->todo & 0x8)==0)
1203             {
1204                 okChildPath("argvA4", filename);
1205             }
1206             else todo_wine
1207             {
1208                 okChildPath("argvA4", filename);
1209             }
1210         }
1211         test++;
1212     }
1213
1214     test=noquotes_tests;
1215     while (test->basename)
1216     {
1217         sprintf(filename, test->basename, tmpdir);
1218         rc=shell_execute(test->verb, filename, NULL, NULL);
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==0)
1232         {
1233             int count;
1234             const char* verb;
1235             char* str;
1236
1237             verb=(test->verb ? test->verb : "Open");
1238             if ((test->todo & 0x4)==0)
1239             {
1240                 okChildString("argvA3", verb);
1241             }
1242             else todo_wine
1243             {
1244                 okChildString("argvA3", verb);
1245             }
1246
1247             count=4;
1248             str=filename;
1249             while (1)
1250             {
1251                 char attrib[18];
1252                 char* space;
1253                 space=strchr(str, ' ');
1254                 if (space)
1255                     *space='\0';
1256                 sprintf(attrib, "argvA%d", count);
1257                 if ((test->todo & 0x8)==0)
1258                 {
1259                     okChildPath(attrib, str);
1260                 }
1261                 else todo_wine
1262                 {
1263                     okChildPath(attrib, str);
1264                 }
1265                 count++;
1266                 if (!space)
1267                     break;
1268                 str=space+1;
1269             }
1270             if ((test->todo & 0x2)==0)
1271             {
1272                 okChildInt("argcA", count);
1273             }
1274             else todo_wine
1275             {
1276                 okChildInt("argcA", count);
1277             }
1278         }
1279         test++;
1280     }
1281
1282     if (dllver.dwMajorVersion != 0)
1283     {
1284         /* The more recent versions of shell32.dll accept quoted filenames
1285          * while older ones (e.g. 4.00) don't. Still we want to test this
1286          * because IE 6 depends on the new behavior.
1287          * One day we may need to check the exact version of the dll but for
1288          * now making sure DllGetVersion() is present is sufficient.
1289          */
1290         sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
1291         rc=shell_execute(NULL, filename, NULL, NULL);
1292         ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
1293            GetLastError());
1294         okChildInt("argcA", 5);
1295         okChildString("argvA3", "Open");
1296         sprintf(filename, "%s\\test file.shlexec", tmpdir);
1297         okChildPath("argvA4", filename);
1298     }
1299 }
1300
1301 static void test_find_executable(void)
1302 {
1303     char filename[MAX_PATH];
1304     char command[MAX_PATH];
1305     const filename_tests_t* test;
1306     INT_PTR rc;
1307
1308     if (!create_test_association(".sfe"))
1309     {
1310         skip("Unable to create association for '.sfe'\n");
1311         return;
1312     }
1313     create_test_verb(".sfe", "Open", 1, "%1");
1314
1315     /* Don't test FindExecutable(..., NULL), it always crashes */
1316
1317     strcpy(command, "your word");
1318     if (0) /* Can crash on Vista! */
1319     {
1320     rc=(INT_PTR)FindExecutableA(NULL, NULL, command);
1321     ok(rc == SE_ERR_FNF || rc > 32 /* nt4 */, "FindExecutable(NULL) returned %ld\n", rc);
1322     ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
1323     }
1324
1325     strcpy(command, "your word");
1326     rc=(INT_PTR)FindExecutableA(tmpdir, NULL, command);
1327     ok(rc == SE_ERR_NOASSOC /* >= win2000 */ || rc > 32 /* win98, nt4 */, "FindExecutable(NULL) returned %ld\n", rc);
1328     ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
1329
1330     sprintf(filename, "%s\\test file.sfe", tmpdir);
1331     rc=(INT_PTR)FindExecutableA(filename, NULL, command);
1332     ok(rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
1333     /* Depending on the platform, command could be '%1' or 'test file.sfe' */
1334
1335     rc=(INT_PTR)FindExecutableA("test file.sfe", tmpdir, command);
1336     ok(rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
1337
1338     rc=(INT_PTR)FindExecutableA("test file.sfe", NULL, command);
1339     ok(rc == SE_ERR_FNF, "FindExecutable(%s) returned %ld\n", filename, rc);
1340
1341     delete_test_association(".sfe");
1342
1343     if (!create_test_association(".shl"))
1344     {
1345         skip("Unable to create association for '.shl'\n");
1346         return;
1347     }
1348     create_test_verb(".shl", "Open", 0, "Open");
1349
1350     sprintf(filename, "%s\\test file.shl", tmpdir);
1351     rc=(INT_PTR)FindExecutableA(filename, NULL, command);
1352     ok(rc == SE_ERR_FNF /* NT4 */ || rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
1353
1354     sprintf(filename, "%s\\test file.shlfoo", tmpdir);
1355     rc=(INT_PTR)FindExecutableA(filename, NULL, command);
1356
1357     delete_test_association(".shl");
1358
1359     if (rc > 32)
1360     {
1361         /* On Windows XP and 2003 FindExecutable() is completely broken.
1362          * Probably what it does is convert the filename to 8.3 format,
1363          * which as a side effect converts the '.shlfoo' extension to '.shl',
1364          * and then tries to find an association for '.shl'. This means it
1365          * will normally fail on most extensions with more than 3 characters,
1366          * like '.mpeg', etc.
1367          * Also it means we cannot do any other test.
1368          */
1369         win_skip("FindExecutable() is broken -> not running 4+ character extension tests\n");
1370         return;
1371     }
1372
1373     test=filename_tests;
1374     while (test->basename)
1375     {
1376         sprintf(filename, test->basename, tmpdir);
1377         if (strchr(filename, '/'))
1378         {
1379             char* c;
1380             c=filename;
1381             while (*c)
1382             {
1383                 if (*c=='\\')
1384                     *c='/';
1385                 c++;
1386             }
1387         }
1388         /* Win98 does not '\0'-terminate command! */
1389         memset(command, '\0', sizeof(command));
1390         rc=(INT_PTR)FindExecutableA(filename, NULL, command);
1391         if (rc > 32)
1392             rc=33;
1393         if ((test->todo & 0x10)==0)
1394         {
1395             ok(rc==test->rc, "FindExecutable(%s) failed: rc=%ld\n", filename, rc);
1396         }
1397         else todo_wine
1398         {
1399             ok(rc==test->rc, "FindExecutable(%s) failed: rc=%ld\n", filename, rc);
1400         }
1401         if (rc > 32)
1402         {
1403             int equal;
1404             equal=strcmp(command, argv0) == 0 ||
1405                 /* NT4 returns an extra 0x8 character! */
1406                 (strlen(command) == strlen(argv0)+1 && strncmp(command, argv0, strlen(argv0)) == 0);
1407             if ((test->todo & 0x20)==0)
1408             {
1409                 ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
1410                    filename, command, argv0);
1411             }
1412             else todo_wine
1413             {
1414                 ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
1415                    filename, command, argv0);
1416             }
1417         }
1418         test++;
1419     }
1420 }
1421
1422
1423 static filename_tests_t lnk_tests[]=
1424 {
1425     /* Pass bad / nonexistent filenames as a parameter */
1426     {NULL, "%s\\nonexistent.shlexec",    0xa, 33},
1427     {NULL, "%s\\nonexistent.noassoc",    0xa, 33},
1428
1429     /* Pass regular paths as a parameter */
1430     {NULL, "%s\\test file.shlexec",      0xa, 33},
1431     {NULL, "%s/%%nasty%% $file.shlexec", 0xa, 33},
1432
1433     /* Pass filenames with no association as a parameter */
1434     {NULL, "%s\\test file.noassoc",      0xa, 33},
1435
1436     {NULL, NULL, 0}
1437 };
1438
1439 static void test_lnks(void)
1440 {
1441     char filename[MAX_PATH];
1442     char params[MAX_PATH];
1443     const filename_tests_t* test;
1444     int rc;
1445
1446     sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
1447     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
1448     ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
1449        GetLastError());
1450     okChildInt("argcA", 5);
1451     okChildString("argvA3", "Open");
1452     sprintf(params, "%s\\test file.shlexec", tmpdir);
1453     get_long_path_name(params, filename, sizeof(filename));
1454     okChildPath("argvA4", filename);
1455
1456     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
1457     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
1458     ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
1459        GetLastError());
1460     okChildInt("argcA", 4);
1461     okChildString("argvA3", "Lnk");
1462
1463     if (dllver.dwMajorVersion>=6)
1464     {
1465         char* c;
1466        /* Recent versions of shell32.dll accept '/'s in shortcut paths.
1467          * Older versions don't or are quite buggy in this regard.
1468          */
1469         sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
1470         c=filename;
1471         while (*c)
1472         {
1473             if (*c=='\\')
1474                 *c='/';
1475             c++;
1476         }
1477         rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, NULL, NULL);
1478         ok(rc > 32, "%s failed: rc=%d err=%d\n", shell_call, rc,
1479            GetLastError());
1480         okChildInt("argcA", 4);
1481         okChildString("argvA3", "Lnk");
1482     }
1483
1484     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
1485     test=lnk_tests;
1486     while (test->basename)
1487     {
1488         params[0]='\"';
1489         sprintf(params+1, test->basename, tmpdir);
1490         strcat(params,"\"");
1491         rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, filename, params,
1492                             NULL);
1493         if (rc > 32)
1494             rc=33;
1495         if ((test->todo & 0x1)==0)
1496         {
1497             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1498                rc, GetLastError());
1499         }
1500         else todo_wine
1501         {
1502             ok(rc==test->rc, "%s failed: rc=%d err=%d\n", shell_call,
1503                rc, GetLastError());
1504         }
1505         if (rc==0)
1506         {
1507             if ((test->todo & 0x2)==0)
1508             {
1509                 okChildInt("argcA", 5);
1510             }
1511             else
1512             {
1513                 okChildInt("argcA", 5);
1514             }
1515             if ((test->todo & 0x4)==0)
1516             {
1517                 okChildString("argvA3", "Lnk");
1518             }
1519             else todo_wine
1520             {
1521                 okChildString("argvA3", "Lnk");
1522             }
1523             sprintf(params, test->basename, tmpdir);
1524             if ((test->todo & 0x8)==0)
1525             {
1526                 okChildPath("argvA4", params);
1527             }
1528             else
1529             {
1530                 okChildPath("argvA4", params);
1531             }
1532         }
1533         test++;
1534     }
1535 }
1536
1537
1538 static void test_exes(void)
1539 {
1540     char filename[MAX_PATH];
1541     char params[1024];
1542     int rc;
1543
1544     sprintf(params, "shlexec \"%s\" Exec", child_file);
1545
1546     /* We need NOZONECHECKS on Win2003 to block a dialog */
1547     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
1548                         NULL);
1549     ok(rc > 32, "%s returned %d\n", shell_call, rc);
1550     okChildInt("argcA", 4);
1551     okChildString("argvA3", "Exec");
1552
1553     if (! skip_noassoc_tests)
1554     {
1555         sprintf(filename, "%s\\test file.noassoc", tmpdir);
1556         if (CopyFile(argv0, filename, FALSE))
1557         {
1558             rc=shell_execute(NULL, filename, params, NULL);
1559             todo_wine {
1560                 ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
1561             }
1562         }
1563     }
1564     else
1565     {
1566         win_skip("Skipping shellexecute of file with unassociated extension\n");
1567     }
1568 }
1569
1570 static void test_exes_long(void)
1571 {
1572     char filename[MAX_PATH];
1573     char params[2024];
1574     char longparam[MAX_PATH];
1575     int rc;
1576
1577     for (rc = 0; rc < MAX_PATH; rc++)
1578         longparam[rc]='a'+rc%26;
1579     longparam[MAX_PATH-1]=0;
1580
1581
1582     sprintf(params, "shlexec \"%s\" %s", child_file,longparam);
1583
1584     /* We need NOZONECHECKS on Win2003 to block a dialog */
1585     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, params,
1586                         NULL);
1587     ok(rc > 32, "%s returned %d\n", shell_call, rc);
1588     okChildInt("argcA", 4);
1589     okChildString("argvA3", longparam);
1590
1591     if (! skip_noassoc_tests)
1592     {
1593         sprintf(filename, "%s\\test file.noassoc", tmpdir);
1594         if (CopyFile(argv0, filename, FALSE))
1595         {
1596             rc=shell_execute(NULL, filename, params, NULL);
1597             todo_wine {
1598                 ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
1599             }
1600         }
1601     }
1602     else
1603     {
1604         win_skip("Skipping shellexecute of file with unassociated extension\n");
1605     }
1606 }
1607
1608 typedef struct
1609 {
1610     const char* command;
1611     const char* ddeexec;
1612     const char* application;
1613     const char* topic;
1614     const char* ifexec;
1615     int expectedArgs;
1616     const char* expectedDdeExec;
1617     int todo;
1618 } dde_tests_t;
1619
1620 static dde_tests_t dde_tests[] =
1621 {
1622     /* Test passing and not passing command-line
1623      * argument, no DDE */
1624     {"", NULL, NULL, NULL, NULL, FALSE, "", 0x0},
1625     {"\"%1\"", NULL, NULL, NULL, NULL, TRUE, "", 0x0},
1626
1627     /* Test passing and not passing command-line
1628      * argument, with DDE */
1629     {"", "[open(\"%1\")]", "shlexec", "dde", NULL, FALSE, "[open(\"%s\")]", 0x0},
1630     {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, TRUE, "[open(\"%s\")]", 0x0},
1631
1632     /* Test unquoted %1 in command and ddeexec
1633      * (test filename has space) */
1634     {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", 0x0},
1635
1636     /* Test ifexec precedence over ddeexec */
1637     {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", FALSE, "[ifexec(\"%s\")]", 0x0},
1638
1639     /* Test default DDE topic */
1640     {"", "[open(\"%1\")]", "shlexec", NULL, NULL, FALSE, "[open(\"%s\")]", 0x0},
1641
1642     /* Test default DDE application */
1643     {"", "[open(\"%1\")]", NULL, "dde", NULL, FALSE, "[open(\"%s\")]", 0x0},
1644
1645     {NULL, NULL, NULL, NULL, NULL, 0, 0x0}
1646 };
1647
1648 static DWORD WINAPI hooked_WaitForInputIdle(HANDLE process, DWORD timeout)
1649 {
1650     return WaitForSingleObject(dde_ready_event, timeout);
1651 }
1652
1653 /*
1654  * WaitForInputIdle() will normally return immediately for console apps. That's
1655  * a problem for us because ShellExecute will assume that an app is ready to
1656  * receive DDE messages after it has called WaitForInputIdle() on that app.
1657  * To work around that we install our own version of WaitForInputIdle() that
1658  * will wait for the child to explicitly tell us that it is ready. We do that
1659  * by changing the entry for WaitForInputIdle() in the shell32 import address
1660  * table.
1661  */
1662 static void hook_WaitForInputIdle(void *new_func)
1663 {
1664     char *base;
1665     PIMAGE_NT_HEADERS nt_headers;
1666     DWORD import_directory_rva;
1667     PIMAGE_IMPORT_DESCRIPTOR import_descriptor;
1668
1669     base = (char *) GetModuleHandleA("shell32.dll");
1670     nt_headers = (PIMAGE_NT_HEADERS)(base + ((PIMAGE_DOS_HEADER) base)->e_lfanew);
1671     import_directory_rva = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
1672
1673     /* Search for the correct imported module by walking the import descriptors */
1674     import_descriptor = (PIMAGE_IMPORT_DESCRIPTOR)(base + import_directory_rva);
1675     while (U(*import_descriptor).OriginalFirstThunk != 0)
1676     {
1677         char *import_module_name;
1678
1679         import_module_name = base + import_descriptor->Name;
1680         if (lstrcmpiA(import_module_name, "user32.dll") == 0 ||
1681             lstrcmpiA(import_module_name, "user32") == 0)
1682         {
1683             PIMAGE_THUNK_DATA int_entry;
1684             PIMAGE_THUNK_DATA iat_entry;
1685
1686             /* The import name table and import address table are two parallel
1687              * arrays. We need the import name table to find the imported
1688              * routine and the import address table to patch the address, so
1689              * walk them side by side */
1690             int_entry = (PIMAGE_THUNK_DATA)(base + U(*import_descriptor).OriginalFirstThunk);
1691             iat_entry = (PIMAGE_THUNK_DATA)(base + import_descriptor->FirstThunk);
1692             while (int_entry->u1.Ordinal != 0)
1693             {
1694                 if (! IMAGE_SNAP_BY_ORDINAL(int_entry->u1.Ordinal))
1695                 {
1696                     PIMAGE_IMPORT_BY_NAME import_by_name;
1697                     import_by_name = (PIMAGE_IMPORT_BY_NAME)(base + int_entry->u1.AddressOfData);
1698                     if (lstrcmpA((char *) import_by_name->Name, "WaitForInputIdle") == 0)
1699                     {
1700                         /* Found the correct routine in the correct imported module. Patch it. */
1701                         DWORD old_prot;
1702                         VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), PAGE_READWRITE, &old_prot);
1703                         iat_entry->u1.Function = (ULONG_PTR) new_func;
1704                         VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), old_prot, &old_prot);
1705                         break;
1706                     }
1707                 }
1708                 int_entry++;
1709                 iat_entry++;
1710             }
1711             break;
1712         }
1713
1714         import_descriptor++;
1715     }
1716 }
1717
1718 static void test_dde(void)
1719 {
1720     char filename[MAX_PATH], defApplication[MAX_PATH];
1721     const dde_tests_t* test;
1722     char params[1024];
1723     int rc;
1724     HANDLE map;
1725     char *shared_block;
1726
1727     hook_WaitForInputIdle((void *) hooked_WaitForInputIdle);
1728
1729     sprintf(filename, "%s\\test file.sde", tmpdir);
1730
1731     /* Default service is application name minus path and extension */
1732     strcpy(defApplication, strrchr(argv0, '\\')+1);
1733     *strchr(defApplication, '.') = 0;
1734
1735     map = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
1736                              4096, "winetest_shlexec_dde_map");
1737     shared_block = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 4096);
1738
1739     test = dde_tests;
1740     while (test->command)
1741     {
1742         if (!create_test_association(".sde"))
1743         {
1744             skip("Unable to create association for '.sde'\n");
1745             return;
1746         }
1747         create_test_verb_dde(".sde", "Open", 0, test->command, test->ddeexec,
1748                              test->application, test->topic, test->ifexec);
1749
1750         if (test->application != NULL || test->topic != NULL)
1751         {
1752             strcpy(shared_block, test->application ? test->application : defApplication);
1753             strcpy(shared_block + strlen(shared_block) + 1, test->topic ? test->topic : SZDDESYS_TOPIC);
1754         }
1755         else
1756         {
1757             shared_block[0] = '\0';
1758             shared_block[1] = '\0';
1759         }
1760         ddeExec[0] = 0;
1761
1762         dde_ready_event = CreateEventA(NULL, FALSE, FALSE, "winetest_shlexec_dde_ready");
1763         rc = shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, filename, NULL, NULL);
1764         CloseHandle(dde_ready_event);
1765         if ((test->todo & 0x1)==0)
1766         {
1767             ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call,
1768                rc, GetLastError());
1769         }
1770         else todo_wine
1771         {
1772             ok(32 < rc, "%s failed: rc=%d err=%d\n", shell_call,
1773                rc, GetLastError());
1774         }
1775         if (32 < rc)
1776         {
1777             if ((test->todo & 0x2)==0)
1778             {
1779                 okChildInt("argcA", test->expectedArgs + 3);
1780             }
1781             else todo_wine
1782             {
1783                 okChildInt("argcA", test->expectedArgs + 3);
1784             }
1785             if (test->expectedArgs == 1)
1786             {
1787                 if ((test->todo & 0x4) == 0)
1788                 {
1789                     okChildPath("argvA3", filename);
1790                 }
1791                 else todo_wine
1792                 {
1793                     okChildPath("argvA3", filename);
1794                 }
1795             }
1796             if ((test->todo & 0x8) == 0)
1797             {
1798                 sprintf(params, test->expectedDdeExec, filename);
1799                 okChildPath("ddeExec", params);
1800             }
1801             else todo_wine
1802             {
1803                 sprintf(params, test->expectedDdeExec, filename);
1804                 okChildPath("ddeExec", params);
1805             }
1806         }
1807
1808         delete_test_association(".sde");
1809         test++;
1810     }
1811
1812     UnmapViewOfFile(shared_block);
1813     CloseHandle(map);
1814     hook_WaitForInputIdle((void *) WaitForInputIdle);
1815 }
1816
1817 #define DDE_DEFAULT_APP_VARIANTS 2
1818 typedef struct
1819 {
1820     const char* command;
1821     const char* expectedDdeApplication[DDE_DEFAULT_APP_VARIANTS];
1822     int todo;
1823     int rc[DDE_DEFAULT_APP_VARIANTS];
1824 } dde_default_app_tests_t;
1825
1826 static dde_default_app_tests_t dde_default_app_tests[] =
1827 {
1828     /* Windows XP and 98 handle default DDE app names in different ways.
1829      * The application name we see in the first test determines the pattern
1830      * of application names and return codes we will look for. */
1831
1832     /* Test unquoted existing filename with a space */
1833     {"%s\\test file.exe", {"test file", "test"}, 0x0, {33, 33}},
1834     {"%s\\test file.exe param", {"test file", "test"}, 0x0, {33, 33}},
1835
1836     /* Test quoted existing filename with a space */
1837     {"\"%s\\test file.exe\"", {"test file", "test file"}, 0x0, {33, 33}},
1838     {"\"%s\\test file.exe\" param", {"test file", "test file"}, 0x0, {33, 33}},
1839
1840     /* Test unquoted filename with a space that doesn't exist, but
1841      * test2.exe does */
1842     {"%s\\test2 file.exe", {"test2", "test2"}, 0x0, {33, 33}},
1843     {"%s\\test2 file.exe param", {"test2", "test2"}, 0x0, {33, 33}},
1844
1845     /* Test quoted filename with a space that does not exist */
1846     {"\"%s\\test2 file.exe\"", {"", "test2 file"}, 0x0, {5, 33}},
1847     {"\"%s\\test2 file.exe\" param", {"", "test2 file"}, 0x0, {5, 33}},
1848
1849     /* Test filename supplied without the extension */
1850     {"%s\\test2", {"test2", "test2"}, 0x0, {33, 33}},
1851     {"%s\\test2 param", {"test2", "test2"}, 0x0, {33, 33}},
1852
1853     /* Test an unquoted nonexistent filename */
1854     {"%s\\notexist.exe", {"", "notexist"}, 0x0, {5, 33}},
1855     {"%s\\notexist.exe param", {"", "notexist"}, 0x0, {5, 33}},
1856
1857     /* Test an application that will be found on the path */
1858     {"cmd", {"cmd", "cmd"}, 0x0, {33, 33}},
1859     {"cmd param", {"cmd", "cmd"}, 0x0, {33, 33}},
1860
1861     /* Test an application that will not be found on the path */
1862     {"xyzwxyzwxyz", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1863     {"xyzwxyzwxyz param", {"", "xyzwxyzwxyz"}, 0x0, {5, 33}},
1864
1865     {NULL, {NULL}, 0, {0}}
1866 };
1867
1868 typedef struct
1869 {
1870     char *filename;
1871     DWORD threadIdParent;
1872 } dde_thread_info_t;
1873
1874 static DWORD CALLBACK ddeThread(LPVOID arg)
1875 {
1876     dde_thread_info_t *info = arg;
1877     assert(info && info->filename);
1878     PostThreadMessage(info->threadIdParent,
1879                       WM_QUIT,
1880                       shell_execute_ex(SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI, NULL, info->filename, NULL, NULL),
1881                       0L);
1882     ExitThread(0);
1883 }
1884
1885 static void test_dde_default_app(void)
1886 {
1887     char filename[MAX_PATH];
1888     HSZ hszApplication;
1889     dde_thread_info_t info = { filename, GetCurrentThreadId() };
1890     const dde_default_app_tests_t* test;
1891     char params[1024];
1892     DWORD threadId;
1893     MSG msg;
1894     int rc, which = 0;
1895
1896     post_quit_on_execute = FALSE;
1897     ddeInst = 0;
1898     rc = DdeInitializeA(&ddeInst, ddeCb, CBF_SKIP_ALLNOTIFICATIONS | CBF_FAIL_ADVISES |
1899                         CBF_FAIL_POKES | CBF_FAIL_REQUESTS, 0L);
1900     assert(rc == DMLERR_NO_ERROR);
1901
1902     sprintf(filename, "%s\\test file.sde", tmpdir);
1903
1904     /* It is strictly not necessary to register an application name here, but wine's
1905      * DdeNameService implementation complains if 0L is passed instead of
1906      * hszApplication with DNS_FILTEROFF */
1907     hszApplication = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
1908     hszTopic = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
1909     assert(hszApplication && hszTopic);
1910     assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_REGISTER | DNS_FILTEROFF));
1911
1912     test = dde_default_app_tests;
1913     while (test->command)
1914     {
1915         if (!create_test_association(".sde"))
1916         {
1917             skip("Unable to create association for '.sde'\n");
1918             return;
1919         }
1920         sprintf(params, test->command, tmpdir);
1921         create_test_verb_dde(".sde", "Open", 1, params, "[test]", NULL,
1922                              "shlexec", NULL);
1923         ddeApplication[0] = 0;
1924
1925         /* No application will be run as we will respond to the first DDE event,
1926          * so don't wait for it */
1927         SetEvent(hEvent);
1928
1929         assert(CreateThread(NULL, 0, ddeThread, &info, 0, &threadId));
1930         while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg);
1931         rc = msg.wParam > 32 ? 33 : msg.wParam;
1932
1933         /* First test, find which set of test data we expect to see */
1934         if (test == dde_default_app_tests)
1935         {
1936             int i;
1937             for (i=0; i<DDE_DEFAULT_APP_VARIANTS; i++)
1938             {
1939                 if (!strcmp(ddeApplication, test->expectedDdeApplication[i]))
1940                 {
1941                     which = i;
1942                     break;
1943                 }
1944             }
1945             if (i == DDE_DEFAULT_APP_VARIANTS)
1946                 skip("Default DDE application test does not match any available results, using first expected data set.\n");
1947         }
1948
1949         if ((test->todo & 0x1)==0)
1950         {
1951             ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
1952                rc, GetLastError());
1953         }
1954         else todo_wine
1955         {
1956             ok(rc==test->rc[which], "%s failed: rc=%d err=%d\n", shell_call,
1957                rc, GetLastError());
1958         }
1959         if (rc == 33)
1960         {
1961             if ((test->todo & 0x2)==0)
1962             {
1963                 ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
1964                    "Expected application '%s', got '%s'\n",
1965                    test->expectedDdeApplication[which], ddeApplication);
1966             }
1967             else todo_wine
1968             {
1969                 ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
1970                    "Expected application '%s', got '%s'\n",
1971                    test->expectedDdeApplication[which], ddeApplication);
1972             }
1973         }
1974
1975         delete_test_association(".sde");
1976         test++;
1977     }
1978
1979     assert(DdeNameService(ddeInst, hszApplication, 0L, DNS_UNREGISTER));
1980     assert(DdeFreeStringHandle(ddeInst, hszTopic));
1981     assert(DdeFreeStringHandle(ddeInst, hszApplication));
1982     assert(DdeUninitialize(ddeInst));
1983 }
1984
1985 static void init_test(void)
1986 {
1987     HMODULE hdll;
1988     HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
1989     char filename[MAX_PATH];
1990     WCHAR lnkfile[MAX_PATH];
1991     char params[1024];
1992     const char* const * testfile;
1993     lnk_desc_t desc;
1994     DWORD rc;
1995     HRESULT r;
1996
1997     hdll=GetModuleHandleA("shell32.dll");
1998     pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
1999     if (pDllGetVersion)
2000     {
2001         dllver.cbSize=sizeof(dllver);
2002         pDllGetVersion(&dllver);
2003         trace("major=%d minor=%d build=%d platform=%d\n",
2004               dllver.dwMajorVersion, dllver.dwMinorVersion,
2005               dllver.dwBuildNumber, dllver.dwPlatformID);
2006     }
2007     else
2008     {
2009         memset(&dllver, 0, sizeof(dllver));
2010     }
2011
2012     r = CoInitialize(NULL);
2013     ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
2014     if (FAILED(r))
2015         exit(1);
2016
2017     rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
2018     assert(rc!=0 && rc<sizeof(argv0));
2019     if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
2020     {
2021         strcat(argv0, ".so");
2022         ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
2023            "unable to find argv0!\n");
2024     }
2025
2026     GetTempPathA(sizeof(filename), filename);
2027     GetTempFileNameA(filename, "wt", 0, tmpdir);
2028     DeleteFileA( tmpdir );
2029     rc = CreateDirectoryA( tmpdir, NULL );
2030     ok( rc, "failed to create %s err %u\n", tmpdir, GetLastError() );
2031     rc = GetTempFileNameA(tmpdir, "wt", 0, child_file);
2032     assert(rc != 0);
2033     init_event(child_file);
2034
2035     /* Set up the test files */
2036     testfile=testfiles;
2037     while (*testfile)
2038     {
2039         HANDLE hfile;
2040
2041         sprintf(filename, *testfile, tmpdir);
2042         hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
2043                      FILE_ATTRIBUTE_NORMAL, NULL);
2044         if (hfile==INVALID_HANDLE_VALUE)
2045         {
2046             trace("unable to create '%s': err=%d\n", filename, GetLastError());
2047             assert(0);
2048         }
2049         CloseHandle(hfile);
2050         testfile++;
2051     }
2052
2053     /* Setup the test shortcuts */
2054     sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
2055     MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
2056     desc.description=NULL;
2057     desc.workdir=NULL;
2058     sprintf(filename, "%s\\test file.shlexec", tmpdir);
2059     desc.path=filename;
2060     desc.pidl=NULL;
2061     desc.arguments="ignored";
2062     desc.showcmd=0;
2063     desc.icon=NULL;
2064     desc.icon_id=0;
2065     desc.hotkey=0;
2066     create_lnk(lnkfile, &desc, 0);
2067
2068     sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
2069     MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
2070     desc.description=NULL;
2071     desc.workdir=NULL;
2072     desc.path=argv0;
2073     desc.pidl=NULL;
2074     sprintf(params, "shlexec \"%s\" Lnk", child_file);
2075     desc.arguments=params;
2076     desc.showcmd=0;
2077     desc.icon=NULL;
2078     desc.icon_id=0;
2079     desc.hotkey=0;
2080     create_lnk(lnkfile, &desc, 0);
2081
2082     /* Create a basic association suitable for most tests */
2083     if (!create_test_association(".shlexec"))
2084     {
2085         skip("Unable to create association for '.shlexec'\n");
2086         return;
2087     }
2088     create_test_verb(".shlexec", "Open", 0, "Open \"%1\"");
2089     create_test_verb(".shlexec", "NoQuotes", 0, "NoQuotes %1");
2090     create_test_verb(".shlexec", "LowerL", 0, "LowerL %l");
2091     create_test_verb(".shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
2092     create_test_verb(".shlexec", "UpperL", 0, "UpperL %L");
2093     create_test_verb(".shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
2094
2095     create_test_verb(".shlexec", "NoQuotesParam2", 0, "NoQuotesParam2 %2");
2096     create_test_verb(".shlexec", "QuotedParam2", 0, "QuotedParam2 \"%2\"");
2097
2098     create_test_verb(".shlexec", "NoQuotesAllParams", 0, "NoQuotesAllParams %*");
2099     create_test_verb(".shlexec", "QuotedAllParams", 0, "QuotedAllParams \"%*\"");
2100
2101     create_test_verb(".shlexec", "NoQuotesParams345etc", 0, "NoQuotesParams345etc %~3");
2102     create_test_verb(".shlexec", "QuotedParams345etc", 0, "QuotedParams345etc \"%~3\"");
2103 }
2104
2105 static void cleanup_test(void)
2106 {
2107     char filename[MAX_PATH];
2108     const char* const * testfile;
2109
2110     /* Delete the test files */
2111     testfile=testfiles;
2112     while (*testfile)
2113     {
2114         sprintf(filename, *testfile, tmpdir);
2115         /* Make sure we can delete the files ('test file.noassoc' is read-only now) */
2116         SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL);
2117         DeleteFile(filename);
2118         testfile++;
2119     }
2120     DeleteFile(child_file);
2121     RemoveDirectoryA(tmpdir);
2122
2123     /* Delete the test association */
2124     delete_test_association(".shlexec");
2125
2126     CloseHandle(hEvent);
2127
2128     CoUninitialize();
2129 }
2130
2131 static void test_commandline(void)
2132 {
2133     static const WCHAR one[] = {'o','n','e',0};
2134     static const WCHAR two[] = {'t','w','o',0};
2135     static const WCHAR three[] = {'t','h','r','e','e',0};
2136     static const WCHAR four[] = {'f','o','u','r',0};
2137
2138     static const WCHAR fmt1[] = {'%','s',' ','%','s',' ','%','s',' ','%','s',0};
2139     static const WCHAR fmt2[] = {' ','%','s',' ','%','s',' ','%','s',' ','%','s',0};
2140     static const WCHAR fmt3[] = {'%','s','=','%','s',' ','%','s','=','\"','%','s','\"',0};
2141     static const WCHAR fmt4[] = {'\"','%','s','\"',' ','\"','%','s',' ','%','s','\"',' ','%','s',0};
2142     static const WCHAR fmt5[] = {'\\','\"','%','s','\"',' ','%','s','=','\"','%','s','\\','\"',' ','\"','%','s','\\','\"',0};
2143     static const WCHAR fmt6[] = {0};
2144
2145     static const WCHAR chkfmt1[] = {'%','s','=','%','s',0};
2146     static const WCHAR chkfmt2[] = {'%','s',' ','%','s',0};
2147     static const WCHAR chkfmt3[] = {'\\','\"','%','s','\"',0};
2148     static const WCHAR chkfmt4[] = {'%','s','=','%','s','\"',' ','%','s','\"',0};
2149     WCHAR cmdline[255];
2150     LPWSTR *args = (LPWSTR*)0xdeadcafe, pbuf;
2151     INT numargs = -1;
2152     size_t buflen;
2153
2154     wsprintfW(cmdline,fmt1,one,two,three,four);
2155     args=CommandLineToArgvW(cmdline,&numargs);
2156     if (args == NULL && numargs == -1)
2157     {
2158         win_skip("CommandLineToArgvW not implemented, skipping\n");
2159         return;
2160     }
2161     ok(numargs == 4, "expected 4 args, got %i\n",numargs);
2162     ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n");
2163     ok(lstrcmpW(args[1],two)==0,"arg1 is not as expected\n");
2164     ok(lstrcmpW(args[2],three)==0,"arg2 is not as expected\n");
2165     ok(lstrcmpW(args[3],four)==0,"arg3 is not as expected\n");
2166
2167     wsprintfW(cmdline,fmt2,one,two,three,four);
2168     args=CommandLineToArgvW(cmdline,&numargs);
2169     ok(numargs == 5, "expected 5 args, got %i\n",numargs);
2170     ok(args[0][0]==0,"arg0 is not as expected\n");
2171     ok(lstrcmpW(args[1],one)==0,"arg1 is not as expected\n");
2172     ok(lstrcmpW(args[2],two)==0,"arg2 is not as expected\n");
2173     ok(lstrcmpW(args[3],three)==0,"arg3 is not as expected\n");
2174     ok(lstrcmpW(args[4],four)==0,"arg4 is not as expected\n");
2175
2176     wsprintfW(cmdline,fmt3,one,two,three,four);
2177     args=CommandLineToArgvW(cmdline,&numargs);
2178     ok(numargs == 2, "expected 2 args, got %i\n",numargs);
2179     wsprintfW(cmdline,chkfmt1,one,two);
2180     ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n");
2181     wsprintfW(cmdline,chkfmt1,three,four);
2182     ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
2183
2184     wsprintfW(cmdline,fmt4,one,two,three,four);
2185     args=CommandLineToArgvW(cmdline,&numargs);
2186     ok(numargs == 3, "expected 3 args, got %i\n",numargs);
2187     ok(lstrcmpW(args[0],one)==0,"arg0 is not as expected\n");
2188     wsprintfW(cmdline,chkfmt2,two,three);
2189     ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
2190     ok(lstrcmpW(args[2],four)==0,"arg2 is not as expected\n");
2191
2192     wsprintfW(cmdline,fmt5,one,two,three,four);
2193     args=CommandLineToArgvW(cmdline,&numargs);
2194     ok(numargs == 2, "expected 2 args, got %i\n",numargs);
2195     wsprintfW(cmdline,chkfmt3,one);
2196     todo_wine ok(lstrcmpW(args[0],cmdline)==0,"arg0 is not as expected\n");
2197     wsprintfW(cmdline,chkfmt4,two,three,four);
2198     todo_wine ok(lstrcmpW(args[1],cmdline)==0,"arg1 is not as expected\n");
2199
2200     wsprintfW(cmdline,fmt6);
2201     args=CommandLineToArgvW(cmdline,&numargs);
2202     ok(numargs == 1, "expected 1 args, got %i\n",numargs);
2203     if (numargs == 1) {
2204         buflen = max(lstrlenW(args[0])+1,256);
2205         pbuf = HeapAlloc(GetProcessHeap(), 0, buflen*sizeof(pbuf[0]));
2206         GetModuleFileNameW(NULL, pbuf, buflen);
2207         pbuf[buflen-1] = 0;
2208         /* check args[0] is module file name */
2209         ok(lstrcmpW(args[0],pbuf)==0, "wrong path to the current executable\n");
2210         HeapFree(GetProcessHeap(), 0, pbuf);
2211     }
2212 }
2213
2214 static void test_directory(void)
2215 {
2216     char path[MAX_PATH], newdir[MAX_PATH];
2217     char params[1024];
2218     int rc;
2219
2220     /* copy this executable to a new folder and cd to it */
2221     sprintf(newdir, "%s\\newfolder", tmpdir);
2222     rc = CreateDirectoryA( newdir, NULL );
2223     ok( rc, "failed to create %s err %u\n", newdir, GetLastError() );
2224     sprintf(path, "%s\\%s", newdir, path_find_file_name(argv0));
2225     CopyFileA(argv0, path, FALSE);
2226     SetCurrentDirectory(tmpdir);
2227
2228     sprintf(params, "shlexec \"%s\" Exec", child_file);
2229
2230     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI,
2231                         NULL, path_find_file_name(argv0), params, NULL);
2232     todo_wine ok(rc == SE_ERR_FNF, "%s returned %d\n", shell_call, rc);
2233
2234     rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_FLAG_NO_UI,
2235                         NULL, path_find_file_name(argv0), params, newdir);
2236     ok(rc > 32, "%s returned %d\n", shell_call, rc);
2237     okChildInt("argcA", 4);
2238     okChildString("argvA3", "Exec");
2239     todo_wine okChildPath("longPath", path);
2240
2241     DeleteFile(path);
2242     RemoveDirectoryA(newdir);
2243 }
2244
2245 START_TEST(shlexec)
2246 {
2247
2248     myARGC = winetest_get_mainargs(&myARGV);
2249     if (myARGC >= 3)
2250     {
2251         doChild(myARGC, myARGV);
2252         exit(0);
2253     }
2254
2255     init_test();
2256
2257     test_argify();
2258     test_lpFile_parsed();
2259     test_filename();
2260     test_find_executable();
2261     test_lnks();
2262     test_exes();
2263     test_exes_long();
2264     test_dde();
2265     test_dde_default_app();
2266     test_commandline();
2267     test_directory();
2268
2269     cleanup_test();
2270 }