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