ReactOS 0.4.15-dev-7788-g1ad9096
shlexec.c
Go to the documentation of this file.
1/*
2 * Unit test of the ShellExecute function.
3 *
4 * Copyright 2005, 2016 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 * - ShellExecuteEx() also calls SetLastError() with meaningful values which
30 * we could check
31 */
32
33#include <stdio.h>
34#include <assert.h>
35
36#include "wtypes.h"
37#include "winbase.h"
38#include "windef.h"
39#include "shellapi.h"
40#include "shlwapi.h"
41#include "ddeml.h"
42
43#include "wine/heap.h"
44#include "wine/test.h"
45
46#include "shell32_test.h"
47
48
49static char argv0[MAX_PATH];
50static int myARGC;
51static char** myARGV;
52static char tmpdir[MAX_PATH];
53static char child_file[MAX_PATH];
58
59
60/***
61 *
62 * Helpers to read from / write to the child process results file.
63 * (borrowed from dlls/kernel32/tests/process.c)
64 *
65 ***/
66
67static const char* encodeA(const char* str)
68{
69 static char encoded[2*1024+1];
70 char* ptr;
71 size_t len,i;
72
73 if (!str) return "";
74 len = strlen(str) + 1;
75 if (len >= sizeof(encoded)/2)
76 {
77 fprintf(stderr, "string is too long!\n");
78 assert(0);
79 }
80 ptr = encoded;
81 for (i = 0; i < len; i++)
82 sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
83 ptr[2 * len] = '\0';
84 return ptr;
85}
86
87static unsigned decode_char(char c)
88{
89 if (c >= '0' && c <= '9') return c - '0';
90 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
91 assert(c >= 'A' && c <= 'F');
92 return c - 'A' + 10;
93}
94
95static char* decodeA(const char* str)
96{
97 static char decoded[1024];
98 char* ptr;
99 size_t len,i;
100
101 len = strlen(str) / 2;
102 if (!len--) return NULL;
103 if (len >= sizeof(decoded))
104 {
105 fprintf(stderr, "string is too long!\n");
106 assert(0);
107 }
108 ptr = decoded;
109 for (i = 0; i < len; i++)
110 ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
111 ptr[len] = '\0';
112 return ptr;
113}
114
115static void WINAPIV WINETEST_PRINTF_ATTR(2,3) childPrintf(HANDLE h, const char* fmt, ...)
116{
118 char buffer[1024];
119 DWORD w;
120
125}
126
127static char* getChildString(const char* sect, const char* key)
128{
129 char buf[1024];
130 char* ret;
131
132 GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), child_file);
133 if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
134 assert(!(strlen(buf) & 1));
135 ret = decodeA(buf);
136 return ret;
137}
138
139
140/***
141 *
142 * Child code
143 *
144 ***/
145
146#define CHILD_DDE_TIMEOUT 2500
148static HSZ hszTopic;
151
152/* Handle DDE for doChild() and test_dde_default_app() */
153static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv,
154 HSZ hsz1, HSZ hsz2, HDDEDATA hData,
155 ULONG_PTR dwData1, ULONG_PTR dwData2)
156{
157 DWORD size = 0;
158
159 if (winetest_debug > 2)
160 trace("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
161 uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
162
163 switch (uType)
164 {
165 case XTYP_CONNECT:
166 if (!DdeCmpStringHandles(hsz1, hszTopic))
167 {
169 ok(size < MAX_PATH, "got size %d\n", size);
171 return (HDDEDATA)TRUE;
172 }
173 return (HDDEDATA)FALSE;
174
175 case XTYP_EXECUTE:
176 size = DdeGetData(hData, (LPBYTE)ddeExec, MAX_PATH, 0);
177 ok(size < MAX_PATH, "got size %d\n", size);
179 DdeFreeDataHandle(hData);
182 return (HDDEDATA)DDE_FACK;
183
184 default:
185 return NULL;
186 }
187}
188
190static void init_event(const char* child_file)
191{
192 char* event_name;
193 event_name=strrchr(child_file, '\\')+1;
194 hEvent=CreateEventA(NULL, FALSE, FALSE, event_name);
195}
196
197/*
198 * This is just to make sure the child won't run forever stuck in a
199 * GetMessage() loop when DDE fails for some reason.
200 */
202{
203 trace("childTimeout called\n");
204
206}
207
208static void doChild(int argc, char** argv)
209{
210 char *filename, buffer[MAX_PATH];
212 int i;
213 UINT_PTR timer;
214
215 filename=argv[2];
218 return;
219
220 /* Arguments */
221 childPrintf(hFile, "[Child]\r\n");
222 if (winetest_debug > 2)
223 {
224 trace("cmdlineA='%s'\n", GetCommandLineA());
225 trace("argcA=%d\n", argc);
226 }
227 childPrintf(hFile, "cmdlineA=%s\r\n", encodeA(GetCommandLineA()));
228 childPrintf(hFile, "argcA=%d\r\n", argc);
229 for (i = 0; i < argc; i++)
230 {
231 if (winetest_debug > 2)
232 trace("argvA%d='%s'\n", i, argv[i]);
233 childPrintf(hFile, "argvA%d=%s\r\n", i, encodeA(argv[i]));
234 }
236 childPrintf(hFile, "longPath=%s\r\n", encodeA(buffer));
237
238 /* Check environment variable inheritance */
239 *buffer = '\0';
240 SetLastError(0);
241 GetEnvironmentVariableA("ShlexecVar", buffer, sizeof(buffer));
242 childPrintf(hFile, "ShlexecVarLE=%d\r\n", GetLastError());
243 childPrintf(hFile, "ShlexecVar=%s\r\n", encodeA(buffer));
244
245 map = OpenFileMappingA(FILE_MAP_READ, FALSE, "winetest_shlexec_dde_map");
246 if (map != NULL)
247 {
248 HANDLE dde_ready;
249 char *shared_block = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 4096);
251 if (shared_block[0] != '\0' || shared_block[1] != '\0')
252 {
253 HDDEDATA hdde;
254 HSZ hszApplication;
255 MSG msg;
256 UINT rc;
257
259 ddeInst = 0;
262 ok(rc == DMLERR_NO_ERROR, "DdeInitializeA() returned %d\n", rc);
263 hszApplication = DdeCreateStringHandleA(ddeInst, shared_block, CP_WINANSI);
264 ok(hszApplication != NULL, "DdeCreateStringHandleA(%s) = NULL\n", shared_block);
265 shared_block += strlen(shared_block) + 1;
267 ok(hszTopic != NULL, "DdeCreateStringHandleA(%s) = NULL\n", shared_block);
268 hdde = DdeNameService(ddeInst, hszApplication, 0, DNS_REGISTER | DNS_FILTEROFF);
269 ok(hdde != NULL, "DdeNameService() failed le=%u\n", GetLastError());
270
272
273 dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready");
274 SetEvent(dde_ready);
275 CloseHandle(dde_ready);
276
277 while (GetMessageA(&msg, NULL, 0, 0))
278 {
279 if (winetest_debug > 2)
280 trace("msg %d lParam=%ld wParam=%lu\n", msg.message, msg.lParam, msg.wParam);
282 }
283
284 Sleep(500);
285 KillTimer(NULL, timer);
286 hdde = DdeNameService(ddeInst, hszApplication, 0, DNS_UNREGISTER);
287 ok(hdde != NULL, "DdeNameService() failed le=%u\n", GetLastError());
288 ok(DdeFreeStringHandle(ddeInst, hszTopic), "DdeFreeStringHandle(topic)\n");
289 ok(DdeFreeStringHandle(ddeInst, hszApplication), "DdeFreeStringHandle(application)\n");
290 ok(DdeUninitialize(ddeInst), "DdeUninitialize() failed\n");
291 }
292 else
293 {
294 dde_ready = OpenEventA(EVENT_MODIFY_STATE, FALSE, "winetest_shlexec_dde_ready");
295 SetEvent(dde_ready);
296 CloseHandle(dde_ready);
297 }
298
299 UnmapViewOfFile(shared_block);
300
301 childPrintf(hFile, "ddeExec=%s\r\n", encodeA(ddeExec));
302 }
303
304 childPrintf(hFile, "Failures=%d\r\n", winetest_get_failures());
306
310}
311
312static void dump_child_(const char* file, int line)
313{
314 if (winetest_debug > 1)
315 {
316 char key[18];
317 char* str;
318 int i, c;
319
320 str=getChildString("Child", "cmdlineA");
321 trace_(file, line)("cmdlineA='%s'\n", str);
322 c=GetPrivateProfileIntA("Child", "argcA", -1, child_file);
323 trace_(file, line)("argcA=%d\n",c);
324 for (i=0;i<c;i++)
325 {
326 sprintf(key, "argvA%d", i);
327 str=getChildString("Child", key);
328 trace_(file, line)("%s='%s'\n", key, str);
329 }
330
331 c=GetPrivateProfileIntA("Child", "ShlexecVarLE", -1, child_file);
332 trace_(file, line)("ShlexecVarLE=%d\n", c);
333 str=getChildString("Child", "ShlexecVar");
334 trace_(file, line)("ShlexecVar='%s'\n", str);
335
336 c=GetPrivateProfileIntA("Child", "Failures", -1, child_file);
337 trace_(file, line)("Failures=%d\n", c);
338 }
339}
340
341
342/***
343 *
344 * Helpers to check the ShellExecute() / child process results.
345 *
346 ***/
347
348static char shell_call[2048];
349static void WINAPIV WINETEST_PRINTF_ATTR(2,3) _okShell(int condition, const char *msg, ...)
350{
352 char buffer[2048];
353
355 strcat(buffer, " ");
360}
361#define okShell_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : _okShell
362#define okShell okShell_(__FILE__, __LINE__)
363
364static char assoc_desc[2048];
366{
367 *assoc_desc = '\0';
368}
369
370static void okChildString_(const char* file, int line, const char* key, const char* expected, const char* bad)
371{
372 char* result;
373 result=getChildString("Child", key);
374 if (!result)
375 {
376 okShell_(file, line)(FALSE, "%s expected '%s', but key not found or empty\n", key, expected);
377 return;
378 }
380 broken(lstrcmpiA(result, bad) == 0),
381 "%s expected '%s', got '%s'\n", key, expected, result);
382}
383#define okChildString(key, expected) okChildString_(__FILE__, __LINE__, (key), (expected), (expected))
384#define okChildStringBroken(key, expected, broken) okChildString_(__FILE__, __LINE__, (key), (expected), (broken))
385
386static int StrCmpPath(const char* s1, const char* s2)
387{
388 if (!s1 && !s2) return 0;
389 if (!s2) return 1;
390 if (!s1) return -1;
391 while (*s1)
392 {
393 if (!*s2)
394 {
395 if (*s1=='.')
396 s1++;
397 return (*s1-*s2);
398 }
399 if ((*s1=='/' || *s1=='\\') && (*s2=='/' || *s2=='\\'))
400 {
401 while (*s1=='/' || *s1=='\\')
402 s1++;
403 while (*s2=='/' || *s2=='\\')
404 s2++;
405 }
406 else if (toupper(*s1)==toupper(*s2))
407 {
408 s1++;
409 s2++;
410 }
411 else
412 {
413 return (*s1-*s2);
414 }
415 }
416 if (*s2=='.')
417 s2++;
418 if (*s2)
419 return -1;
420 return 0;
421}
422
423static void okChildPath_(const char* file, int line, const char* key, const char* expected)
424{
425 char* result;
426 int equal, shortequal;
427 result=getChildString("Child", key);
428 if (!result)
429 {
430 okShell_(file,line)(FALSE, "%s expected '%s', but key not found or empty\n", key, expected);
431 return;
432 }
433 shortequal = FALSE;
434 equal = (StrCmpPath(result, expected) == 0);
435 if (!equal)
436 {
437 char altpath[MAX_PATH];
438 DWORD rc = GetLongPathNameA(expected, altpath, sizeof(altpath));
439 if (0 < rc && rc < sizeof(altpath))
440 equal = (StrCmpPath(result, altpath) == 0);
441 if (!equal)
442 {
443 rc = GetShortPathNameA(expected, altpath, sizeof(altpath));
444 if (0 < rc && rc < sizeof(altpath))
445 shortequal = (StrCmpPath(result, altpath) == 0);
446 }
447 }
448 okShell_(file,line)(equal || broken(shortequal) /* XP SP1 */,
449 "%s expected '%s', got '%s'\n", key, expected, result);
450}
451#define okChildPath(key, expected) okChildPath_(__FILE__, __LINE__, (key), (expected))
452
453static void okChildInt_(const char* file, int line, const char* key, int expected)
454{
455 INT result;
458 "%s expected %d, but got %d\n", key, expected, result);
459}
460#define okChildInt(key, expected) okChildInt_(__FILE__, __LINE__, (key), (expected))
461
462static void okChildIntBroken_(const char* file, int line, const char* key, int expected)
463{
464 INT result;
467 "%s expected %d, but got %d\n", key, expected, result);
468}
469#define okChildIntBroken(key, expected) okChildIntBroken_(__FILE__, __LINE__, (key), (expected))
470
471
472/***
473 *
474 * ShellExecute wrappers
475 *
476 ***/
477
478static void strcat_param(char* str, const char* name, const char* param)
479{
480 if (param)
481 {
482 if (str[strlen(str)-1] == '"')
483 strcat(str, ", ");
484 strcat(str, name);
485 strcat(str, "=\"");
486 strcat(str, param);
487 strcat(str, "\"");
488 }
489}
490
491static int _todo_wait = 0;
492#define todo_wait for (_todo_wait = 1; _todo_wait; _todo_wait = 0)
493
494static int bad_shellexecute = 0;
495
496static INT_PTR shell_execute_(const char* file, int line, LPCSTR verb, LPCSTR filename, LPCSTR parameters, LPCSTR directory)
497{
498 INT_PTR rc, rcEmpty = 0;
499
500 if(!verb)
501 rcEmpty = shell_execute_(file, line, "", filename, parameters, directory);
502
503 strcpy(shell_call, "ShellExecute(");
504 strcat_param(shell_call, "verb", verb);
506 strcat_param(shell_call, "params", parameters);
508 strcat(shell_call, ")");
510 if (winetest_debug > 1)
511 trace_(file, line)("Called %s\n", shell_call);
512
514 SetLastError(0xcafebabe);
515
516 /* FIXME: We cannot use ShellExecuteEx() here because if there is no
517 * association it displays the 'Open With' dialog and I could not find
518 * a flag to prevent this.
519 */
520 rc=(INT_PTR)ShellExecuteA(NULL, verb, filename, parameters, directory, SW_HIDE);
521
522 if (rc > 32)
523 {
524 int wait_rc;
525 wait_rc=WaitForSingleObject(hEvent, 5000);
526 if (wait_rc == WAIT_TIMEOUT)
527 {
528 HWND wnd = FindWindowA("#32770", "Windows");
529 if (!wnd)
530 wnd = FindWindowA("Shell_Flyout", "");
531 if (wnd != NULL)
532 {
533 SendMessageA(wnd, WM_CLOSE, 0, 0);
534 win_skip("Skipping shellexecute of file with unassociated extension\n");
536 rc = SE_ERR_NOASSOC;
537 }
538 }
540 okShell_(file, line)(wait_rc==WAIT_OBJECT_0 || rc <= 32,
541 "WaitForSingleObject returned %d\n", wait_rc);
542 }
543 /* The child process may have changed the result file, so let profile
544 * functions know about it
545 */
548 {
549 int c;
551 c = GetPrivateProfileIntA("Child", "Failures", -1, child_file);
552 if (c > 0)
554 okChildInt_(file, line, "ShlexecVarLE", 0);
555 okChildString_(file, line, "ShlexecVar", "Present", "Present");
556 }
557
558 if(!verb)
559 {
560 if (rc != rcEmpty && rcEmpty == SE_ERR_NOASSOC) /* NT4 */
562 okShell_(file, line)(rc == rcEmpty ||
563 broken(rc != rcEmpty && rcEmpty == SE_ERR_NOASSOC) /* NT4 */,
564 "Got different return value with empty string: %lu %lu\n", rc, rcEmpty);
565 }
566
567 return rc;
568}
569#define shell_execute(verb, filename, parameters, directory) \
570 shell_execute_(__FILE__, __LINE__, verb, filename, parameters, directory)
571
572static INT_PTR shell_execute_ex_(const char* file, int line,
574 LPCSTR parameters, LPCSTR directory,
575 LPCSTR class)
576{
577 char smask[11];
580 INT_PTR rc;
581
582 /* Add some flags so we can wait for the child process */
584
585 strcpy(shell_call, "ShellExecuteEx(");
586 sprintf(smask, "0x%x", mask);
587 strcat_param(shell_call, "mask", smask);
588 strcat_param(shell_call, "verb", verb);
590 strcat_param(shell_call, "params", parameters);
592 strcat_param(shell_call, "class", class);
593 strcat(shell_call, ")");
595 if (winetest_debug > 1)
596 trace_(file, line)("Called %s\n", shell_call);
597
598 sei.cbSize=sizeof(sei);
599 sei.fMask=mask;
600 sei.hwnd=NULL;
601 sei.lpVerb=verb;
602 sei.lpFile=filename;
603 sei.lpParameters=parameters;
606 sei.hInstApp=NULL; /* Out */
607 sei.lpIDList=NULL;
608 sei.lpClass=class;
609 sei.hkeyClass=NULL;
610 sei.dwHotKey=0;
611 U(sei).hIcon=NULL;
612 sei.hProcess=(HANDLE)0xdeadbeef; /* Out */
613
615 SetLastError(0xcafebabe);
617 rc=(INT_PTR)sei.hInstApp;
618 okShell_(file, line)((success && rc > 32) || (!success && rc <= 32),
619 "rc=%d and hInstApp=%ld is not allowed\n",
620 success, rc);
621
622 if (rc > 32)
623 {
624 DWORD wait_rc, rc;
625 if (sei.hProcess!=NULL)
626 {
627 wait_rc=WaitForSingleObject(sei.hProcess, 5000);
628 okShell_(file, line)(wait_rc==WAIT_OBJECT_0,
629 "WaitForSingleObject(hProcess) returned %d\n",
630 wait_rc);
631 wait_rc = GetExitCodeProcess(sei.hProcess, &rc);
632 okShell_(file, line)(wait_rc, "GetExitCodeProcess() failed le=%u\n", GetLastError());
634 okShell_(file, line)(rc == 0, "child returned %u\n", rc);
636 }
637 wait_rc=WaitForSingleObject(hEvent, 5000);
639 okShell_(file, line)(wait_rc==WAIT_OBJECT_0,
640 "WaitForSingleObject returned %d\n", wait_rc);
641 }
642 else
644 "returned a process handle %p\n", sei.hProcess);
645
646 /* The child process may have changed the result file, so let profile
647 * functions know about it
648 */
651 {
652 int c;
654 c = GetPrivateProfileIntA("Child", "Failures", -1, child_file);
655 if (c > 0)
657 /* When NOZONECHECKS is specified the environment variables are not
658 * inherited if the process does not have elevated privileges.
659 */
661 {
662 okChildInt_(file, line, "ShlexecVarLE", 203);
663 okChildString_(file, line, "ShlexecVar", "", "");
664 }
665 else
666 {
667 okChildInt_(file, line, "ShlexecVarLE", 0);
668 okChildString_(file, line, "ShlexecVar", "Present", "Present");
669 }
670 }
671
672 return rc;
673}
674#define shell_execute_ex(mask, verb, filename, parameters, directory, class) \
675 shell_execute_ex_(__FILE__, __LINE__, mask, verb, filename, parameters, directory, class)
676
677
678/***
679 *
680 * Functions to create / delete associations wrappers
681 *
682 ***/
683
684static BOOL create_test_class(const char* class, BOOL protocol)
685{
686 HKEY hkey, hkey_shell;
687 LONG rc;
688
689 rc = RegCreateKeyExA(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
691 &hkey, NULL);
692 ok(rc == ERROR_SUCCESS || rc == ERROR_ACCESS_DENIED,
693 "could not create class %s (rc=%d)\n", class, rc);
694 if (rc != ERROR_SUCCESS)
695 return FALSE;
696
697 if (protocol)
698 {
699 rc = RegSetValueExA(hkey, "URL Protocol", 0, REG_SZ, (LPBYTE)"", 1);
700 ok(rc == ERROR_SUCCESS, "RegSetValueEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc);
701 }
702
703 rc = RegCreateKeyExA(hkey, "shell", 0, NULL, 0,
704 KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
705 ok(rc == ERROR_SUCCESS, "RegCreateKeyEx 'shell' failed, expected ERROR_SUCCESS, got %d\n", rc);
706
707 CloseHandle(hkey);
708 CloseHandle(hkey_shell);
709 return TRUE;
710}
711
712static BOOL create_test_association(const char* extension)
713{
714 HKEY hkey;
715 char class[MAX_PATH];
716 LONG rc;
717
718 sprintf(class, "shlexec%s", extension);
720 NULL, &hkey, NULL);
721 ok(rc == ERROR_SUCCESS || rc == ERROR_ACCESS_DENIED,
722 "could not create association %s (rc=%d)\n", class, rc);
723 if (rc != ERROR_SUCCESS)
724 return FALSE;
725
726 rc=RegSetValueExA(hkey, NULL, 0, REG_SZ, (LPBYTE) class, strlen(class)+1);
727 ok(rc==ERROR_SUCCESS, "RegSetValueEx '%s' failed, expected ERROR_SUCCESS, got %d\n", class, rc);
728 CloseHandle(hkey);
729
730 return create_test_class(class, FALSE);
731}
732
733/* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
735{
736 LONG ret;
737 DWORD dwMaxSubkeyLen, dwMaxValueLen;
738 DWORD dwMaxLen, dwSize;
739 CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
740 HKEY hSubKey = hKey;
741
742 if(lpszSubKey)
743 {
744 ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
745 if (ret) return ret;
746 }
747
748 /* Get highest length for keys, values */
749 ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
750 &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
751 if (ret) goto cleanup;
752
753 dwMaxSubkeyLen++;
754 dwMaxValueLen++;
755 dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
756 if (dwMaxLen > ARRAY_SIZE(szNameBuf))
757 {
758 /* Name too big: alloc a buffer for it */
759 if (!(lpszName = heap_alloc(dwMaxLen*sizeof(CHAR))))
760 {
762 goto cleanup;
763 }
764 }
765
766
767 /* Recursively delete all the subkeys */
768 while (TRUE)
769 {
770 dwSize = dwMaxLen;
771 if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
772 NULL, NULL, NULL)) break;
773
774 ret = myRegDeleteTreeA(hSubKey, lpszName);
775 if (ret) goto cleanup;
776 }
777
778 if (lpszSubKey)
779 ret = RegDeleteKeyA(hKey, lpszSubKey);
780 else
781 while (TRUE)
782 {
783 dwSize = dwMaxLen;
784 if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
785 NULL, NULL, NULL, NULL)) break;
786
787 ret = RegDeleteValueA(hKey, lpszName);
788 if (ret) goto cleanup;
789 }
790
791cleanup:
792 /* Free buffer if allocated */
793 if (lpszName != szNameBuf)
794 heap_free(lpszName);
795 if(lpszSubKey)
796 RegCloseKey(hSubKey);
797 return ret;
798}
799
800static void delete_test_class(const char* classname)
801{
803}
804
805static void delete_test_association(const char* extension)
806{
807 char classname[MAX_PATH];
808
809 sprintf(classname, "shlexec%s", extension);
812}
813
814static void create_test_verb_dde(const char* classname, const char* verb,
815 int rawcmd, const char* cmdtail, const char *ddeexec,
816 const char *application, const char *topic,
817 const char *ifexec)
818{
819 HKEY hkey_shell, hkey_verb, hkey_cmd;
820 char shell[MAX_PATH];
821 char* cmd;
822 LONG rc;
823
824 strcpy(assoc_desc, " Assoc ");
826 strcat_param(assoc_desc, "verb", verb);
827 sprintf(shell, "%d", rawcmd);
828 strcat_param(assoc_desc, "rawcmd", shell);
829 strcat_param(assoc_desc, "cmdtail", cmdtail);
830 strcat_param(assoc_desc, "ddeexec", ddeexec);
831 strcat_param(assoc_desc, "app", application);
832 strcat_param(assoc_desc, "topic", topic);
833 strcat_param(assoc_desc, "ifexec", ifexec);
834
835 sprintf(shell, "%s\\shell", classname);
837 KEY_CREATE_SUB_KEY, &hkey_shell);
838 ok(rc == ERROR_SUCCESS, "%s key creation failed with %d\n", shell, rc);
839
840 rc=RegCreateKeyExA(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
841 NULL, &hkey_verb, NULL);
842 ok(rc == ERROR_SUCCESS, "%s verb key creation failed with %d\n", verb, rc);
843
844 rc=RegCreateKeyExA(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
845 NULL, &hkey_cmd, NULL);
846 ok(rc == ERROR_SUCCESS, "\'command\' key creation failed with %d\n", rc);
847
848 if (rawcmd)
849 {
850 rc=RegSetValueExA(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmdtail, strlen(cmdtail)+1);
851 }
852 else
853 {
854 cmd = heap_alloc(strlen(argv0) + 10 + strlen(child_file) + 2 + strlen(cmdtail) + 1);
855 sprintf(cmd,"%s shlexec \"%s\" %s", argv0, child_file, cmdtail);
856 rc=RegSetValueExA(hkey_cmd, NULL, 0, REG_SZ, (LPBYTE)cmd, strlen(cmd)+1);
857 ok(rc == ERROR_SUCCESS, "setting command failed with %d\n", rc);
858 heap_free(cmd);
859 }
860
861 if (ddeexec)
862 {
863 HKEY hkey_ddeexec, hkey_application, hkey_topic, hkey_ifexec;
864
865 rc=RegCreateKeyExA(hkey_verb, "ddeexec", 0, NULL, 0, KEY_SET_VALUE |
866 KEY_CREATE_SUB_KEY, NULL, &hkey_ddeexec, NULL);
867 ok(rc == ERROR_SUCCESS, "\'ddeexec\' key creation failed with %d\n", rc);
868 rc=RegSetValueExA(hkey_ddeexec, NULL, 0, REG_SZ, (LPBYTE)ddeexec,
869 strlen(ddeexec)+1);
870 ok(rc == ERROR_SUCCESS, "set value failed with %d\n", rc);
871
872 if (application)
873 {
874 rc=RegCreateKeyExA(hkey_ddeexec, "application", 0, NULL, 0, KEY_SET_VALUE,
875 NULL, &hkey_application, NULL);
876 ok(rc == ERROR_SUCCESS, "\'application\' key creation failed with %d\n", rc);
877
878 rc=RegSetValueExA(hkey_application, NULL, 0, REG_SZ, (LPBYTE)application,
879 strlen(application)+1);
880 ok(rc == ERROR_SUCCESS, "set value failed with %d\n", rc);
881 CloseHandle(hkey_application);
882 }
883 if (topic)
884 {
885 rc=RegCreateKeyExA(hkey_ddeexec, "topic", 0, NULL, 0, KEY_SET_VALUE,
886 NULL, &hkey_topic, NULL);
887 ok(rc == ERROR_SUCCESS, "\'topic\' key creation failed with %d\n", rc);
888 rc=RegSetValueExA(hkey_topic, NULL, 0, REG_SZ, (LPBYTE)topic,
889 strlen(topic)+1);
890 ok(rc == ERROR_SUCCESS, "set value failed with %d\n", rc);
891 CloseHandle(hkey_topic);
892 }
893 if (ifexec)
894 {
895 rc=RegCreateKeyExA(hkey_ddeexec, "ifexec", 0, NULL, 0, KEY_SET_VALUE,
896 NULL, &hkey_ifexec, NULL);
897 ok(rc == ERROR_SUCCESS, "\'ifexec\' key creation failed with %d\n", rc);
898 rc=RegSetValueExA(hkey_ifexec, NULL, 0, REG_SZ, (LPBYTE)ifexec,
899 strlen(ifexec)+1);
900 ok(rc == ERROR_SUCCESS, "set value failed with %d\n", rc);
901 CloseHandle(hkey_ifexec);
902 }
903 CloseHandle(hkey_ddeexec);
904 }
905
906 CloseHandle(hkey_shell);
907 CloseHandle(hkey_verb);
908 CloseHandle(hkey_cmd);
909}
910
911/* Creates a class' non-DDE test verb.
912 * This function is meant to be used to create long term test verbs and thus
913 * does not trace them.
914 */
915static void create_test_verb(const char* classname, const char* verb,
916 int rawcmd, const char* cmdtail)
917{
918 create_test_verb_dde(classname, verb, rawcmd, cmdtail, NULL, NULL,
919 NULL, NULL);
921}
922
923
924/***
925 *
926 * GetLongPathNameA equivalent that supports Win95 and WinNT
927 *
928 ***/
929
930static DWORD get_long_path_name(const char* shortpath, char* longpath, DWORD longlen)
931{
932 char tmplongpath[MAX_PATH];
933 const char* p;
934 DWORD sp = 0, lp = 0;
935 DWORD tmplen;
937 HANDLE goit;
938
939 if (!shortpath || !shortpath[0])
940 return 0;
941
942 if (shortpath[1] == ':')
943 {
944 tmplongpath[0] = shortpath[0];
945 tmplongpath[1] = ':';
946 lp = sp = 2;
947 }
948
949 while (shortpath[sp])
950 {
951 /* check for path delimiters and reproduce them */
952 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
953 {
954 if (!lp || tmplongpath[lp-1] != '\\')
955 {
956 /* strip double "\\" */
957 tmplongpath[lp++] = '\\';
958 }
959 tmplongpath[lp] = 0; /* terminate string */
960 sp++;
961 continue;
962 }
963
964 p = shortpath + sp;
965 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
966 {
967 tmplongpath[lp++] = *p++;
968 tmplongpath[lp++] = *p++;
969 }
970 for (; *p && *p != '/' && *p != '\\'; p++);
971 tmplen = p - (shortpath + sp);
972 lstrcpynA(tmplongpath + lp, shortpath + sp, tmplen + 1);
973 /* Check if the file exists and use the existing file name */
974 goit = FindFirstFileA(tmplongpath, &wfd);
975 if (goit == INVALID_HANDLE_VALUE)
976 return 0;
977 FindClose(goit);
978 strcpy(tmplongpath + lp, wfd.cFileName);
979 lp += strlen(tmplongpath + lp);
980 sp += tmplen;
981 }
982 tmplen = strlen(shortpath) - 1;
983 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
984 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
985 tmplongpath[lp++] = shortpath[tmplen];
986 tmplongpath[lp] = 0;
987
988 tmplen = strlen(tmplongpath) + 1;
989 if (tmplen <= longlen)
990 {
991 strcpy(longpath, tmplongpath);
992 tmplen--; /* length without 0 */
993 }
994
995 return tmplen;
996}
997
998
999/***
1000 *
1001 * Tests
1002 *
1003 ***/
1004
1005static const char* testfiles[]=
1006{
1007 "%s\\test file.shlexec",
1008 "%s\\%%nasty%% $file.shlexec",
1009 "%s\\test file.noassoc",
1010 "%s\\test file.noassoc.shlexec",
1011 "%s\\test file.shlexec.noassoc",
1012 "%s\\test_shortcut_shlexec.lnk",
1013 "%s\\test_shortcut_exe.lnk",
1014 "%s\\test file.shl",
1015 "%s\\test file.shlfoo",
1016 "%s\\test file.sha",
1017 "%s\\test file.sfe",
1018 "%s\\test file.shlproto",
1019 "%s\\masked file.shlexec",
1020 "%s\\masked",
1021 "%s\\test file.sde",
1022 "%s\\test file.exe",
1023 "%s\\test2.exe",
1024 "%s\\simple.shlexec",
1025 "%s\\drawback_file.noassoc",
1026 "%s\\drawback_file.noassoc foo.shlexec",
1027 "%s\\drawback_nonexist.noassoc foo.shlexec",
1028 NULL
1029};
1030
1031typedef struct
1032{
1033 const char* verb;
1034 const char* basename;
1035 int todo;
1038
1040{
1041 /* Test bad / nonexistent filenames */
1042 {NULL, "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF},
1043 {NULL, "%s\\nonexistent.noassoc", 0x0, SE_ERR_FNF},
1044
1045 /* Standard tests */
1046 {NULL, "%s\\test file.shlexec", 0x0, 33},
1047 {NULL, "%s\\test file.shlexec.", 0x0, 33},
1048 {NULL, "%s\\%%nasty%% $file.shlexec", 0x0, 33},
1049 {NULL, "%s/test file.shlexec", 0x0, 33},
1050
1051 /* Test filenames with no association */
1052 {NULL, "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC},
1053
1054 /* Test double extensions */
1055 {NULL, "%s\\test file.noassoc.shlexec", 0x0, 33},
1056 {NULL, "%s\\test file.shlexec.noassoc", 0x0, SE_ERR_NOASSOC},
1057
1058 /* Test alternate verbs */
1059 {"LowerL", "%s\\nonexistent.shlexec", 0x0, SE_ERR_FNF},
1060 {"LowerL", "%s\\test file.noassoc", 0x0, SE_ERR_NOASSOC},
1061
1062 {"QuotedLowerL", "%s\\test file.shlexec", 0x0, 33},
1063 {"QuotedUpperL", "%s\\test file.shlexec", 0x0, 33},
1064
1065 {"notaverb", "%s\\test file.shlexec", 0x10, SE_ERR_NOASSOC},
1066
1067 {"averb", "%s\\test file.sha", 0x10, 33},
1068
1069 /* Test file masked due to space */
1070 {NULL, "%s\\masked file.shlexec", 0x0, 33},
1071 /* Test if quoting prevents the masking */
1072 {NULL, "%s\\masked file.shlexec", 0x40, 33},
1073 /* Test with incorrect quote */
1074 {NULL, "\"%s\\masked file.shlexec", 0x0, SE_ERR_FNF},
1075
1076 /* Test extension / URI protocol collision */
1077 {NULL, "%s\\test file.shlproto", 0x0, SE_ERR_NOASSOC},
1078
1079 {NULL, NULL, 0}
1080};
1081
1083{
1084 /* Test unquoted '%1' thingies */
1085 {"NoQuotes", "%s\\test file.shlexec", 0xa, 33},
1086 {"LowerL", "%s\\test file.shlexec", 0xa, 33},
1087 {"UpperL", "%s\\test file.shlexec", 0xa, 33},
1088
1089 {NULL, NULL, 0}
1090};
1091
1092static void test_lpFile_parsed(void)
1093{
1094 char fileA[MAX_PATH];
1095 INT_PTR rc;
1096
1098 {
1099 skip("No filename parsing tests due to lack of .shlexec association\n");
1100 return;
1101 }
1102
1103 /* existing "drawback_file.noassoc" prevents finding "drawback_file.noassoc foo.shlexec" on wine */
1104 sprintf(fileA, "%s\\drawback_file.noassoc foo.shlexec", tmpdir);
1105 rc=shell_execute(NULL, fileA, NULL, NULL);
1106 okShell(rc > 32, "failed: rc=%lu\n", rc);
1107
1108 /* if quoted, existing "drawback_file.noassoc" not prevents finding "drawback_file.noassoc foo.shlexec" on wine */
1109 sprintf(fileA, "\"%s\\drawback_file.noassoc foo.shlexec\"", tmpdir);
1110 rc=shell_execute(NULL, fileA, NULL, NULL);
1111 okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */,
1112 "failed: rc=%lu\n", rc);
1113
1114 /* error should be SE_ERR_FNF, not SE_ERR_NOASSOC */
1115 sprintf(fileA, "\"%s\\drawback_file.noassoc\" foo.shlexec", tmpdir);
1116 rc=shell_execute(NULL, fileA, NULL, NULL);
1117 okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
1118
1119 /* ""command"" not works on wine (and real win9x and w2k) */
1120 sprintf(fileA, "\"\"%s\\simple.shlexec\"\"", tmpdir);
1121 rc=shell_execute(NULL, fileA, NULL, NULL);
1122 todo_wine okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win9x/2000 */,
1123 "failed: rc=%lu\n", rc);
1124
1125 /* nonexisting "drawback_nonexist.noassoc" not prevents finding "drawback_nonexist.noassoc foo.shlexec" on wine */
1126 sprintf(fileA, "%s\\drawback_nonexist.noassoc foo.shlexec", tmpdir);
1127 rc=shell_execute(NULL, fileA, NULL, NULL);
1128 okShell(rc > 32, "failed: rc=%lu\n", rc);
1129
1130 /* is SEE_MASK_DOENVSUBST default flag? Should only be when XP emulates 9x (XP bug or real 95 or ME behavior ?) */
1131 rc=shell_execute(NULL, "%TMPDIR%\\simple.shlexec", NULL, NULL);
1132 todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
1133
1134 /* quoted */
1135 rc=shell_execute(NULL, "\"%TMPDIR%\\simple.shlexec\"", NULL, NULL);
1136 todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
1137
1138 /* test SEE_MASK_DOENVSUBST works */
1140 NULL, "%TMPDIR%\\simple.shlexec", NULL, NULL, NULL);
1141 okShell(rc > 32, "failed: rc=%lu\n", rc);
1142
1143 /* quoted lpFile does not work on real win95 and nt4 */
1145 NULL, "\"%TMPDIR%\\simple.shlexec\"", NULL, NULL, NULL);
1146 okShell(rc > 32 || broken(rc == SE_ERR_FNF) /* Win95/NT4 */,
1147 "failed: rc=%lu\n", rc);
1148}
1149
1150typedef struct
1151{
1152 const char* cmd;
1153 const char* args[11];
1154 int todo;
1156
1158{
1159 {"exe",
1160 {"exe", NULL}, 0},
1161
1162 {"exe arg1 arg2 \"arg three\" 'four five` six\\ $even)",
1163 {"exe", "arg1", "arg2", "arg three", "'four", "five`", "six\\", "$even)", NULL}, 0},
1164
1165 {"exe arg=1 arg-2 three\tfour\rfour\nfour ",
1166 {"exe", "arg=1", "arg-2", "three", "four\rfour\nfour", NULL}, 0},
1167
1168 {"exe arg\"one\" \"second\"arg thirdarg ",
1169 {"exe", "argone", "secondarg", "thirdarg", NULL}, 0},
1170
1171 /* Don't lose unclosed quoted arguments */
1172 {"exe arg1 \"unclosed",
1173 {"exe", "arg1", "unclosed", NULL}, 0},
1174
1175 {"exe arg1 \"",
1176 {"exe", "arg1", "", NULL}, 0},
1177
1178 /* cmd's metacharacters have no special meaning */
1179 {"exe \"one^\" \"arg\"&two three|four",
1180 {"exe", "one^", "arg&two", "three|four", NULL}, 0},
1181
1182 /* Environment variables are not interpreted either */
1183 {"exe %TMPDIR% %2",
1184 {"exe", "%TMPDIR%", "%2", NULL}, 0},
1185
1186 /* If not followed by a quote, backslashes go through as is */
1187 {"exe o\\ne t\\\\wo t\\\\\\ree f\\\\\\\\our ",
1188 {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0},
1189
1190 {"exe \"o\\ne\" \"t\\\\wo\" \"t\\\\\\ree\" \"f\\\\\\\\our\" ",
1191 {"exe", "o\\ne", "t\\\\wo", "t\\\\\\ree", "f\\\\\\\\our", NULL}, 0},
1192
1193 /* When followed by a quote their number is halved and the remainder
1194 * escapes the quote
1195 */
1196 {"exe \\\"one \\\\\"two\" \\\\\\\"three \\\\\\\\\"four\" end",
1197 {"exe", "\"one", "\\two", "\\\"three", "\\\\four", "end", NULL}, 0},
1198
1199 {"exe \"one\\\" still\" \"two\\\\\" \"three\\\\\\\" still\" \"four\\\\\\\\\" end",
1200 {"exe", "one\" still", "two\\", "three\\\" still", "four\\\\", "end", NULL}, 0},
1201
1202 /* One can put a quote in an unquoted string by tripling it, that is in
1203 * effect quoting it like so """ -> ". The general rule is as follows:
1204 * 3n quotes -> n quotes
1205 * 3n+1 quotes -> n quotes plus start of a quoted string
1206 * 3n+2 quotes -> n quotes (plus an empty string from the remaining pair)
1207 * Nicely, when n is 0 we get the standard rules back.
1208 */
1209 {"exe two\"\"quotes next",
1210 {"exe", "twoquotes", "next", NULL}, 0},
1211
1212 {"exe three\"\"\"quotes next",
1213 {"exe", "three\"quotes", "next", NULL}, 0},
1214
1215 {"exe four\"\"\"\" quotes\" next 4%3=1",
1216 {"exe", "four\" quotes", "next", "4%3=1", NULL}, 0},
1217
1218 {"exe five\"\"\"\"\"quotes next",
1219 {"exe", "five\"quotes", "next", NULL}, 0},
1220
1221 {"exe six\"\"\"\"\"\"quotes next",
1222 {"exe", "six\"\"quotes", "next", NULL}, 0},
1223
1224 {"exe seven\"\"\"\"\"\"\" quotes\" next 7%3=1",
1225 {"exe", "seven\"\" quotes", "next", "7%3=1", NULL}, 0},
1226
1227 {"exe twelve\"\"\"\"\"\"\"\"\"\"\"\"quotes next",
1228 {"exe", "twelve\"\"\"\"quotes", "next", NULL}, 0},
1229
1230 {"exe thirteen\"\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1",
1231 {"exe", "thirteen\"\"\"\" quotes", "next", "13%3=1", NULL}, 0},
1232
1233 /* Inside a quoted string the opening quote is added to the set of
1234 * consecutive quotes to get the effective quotes count. This gives:
1235 * 1+3n quotes -> n quotes
1236 * 1+3n+1 quotes -> n quotes plus closes the quoted string
1237 * 1+3n+2 quotes -> n+1 quotes plus closes the quoted string
1238 */
1239 {"exe \"two\"\"quotes next",
1240 {"exe", "two\"quotes", "next", NULL}, 0},
1241
1242 {"exe \"two\"\" next",
1243 {"exe", "two\"", "next", NULL}, 0},
1244
1245 {"exe \"three\"\"\" quotes\" next 4%3=1",
1246 {"exe", "three\" quotes", "next", "4%3=1", NULL}, 0},
1247
1248 {"exe \"four\"\"\"\"quotes next",
1249 {"exe", "four\"quotes", "next", NULL}, 0},
1250
1251 {"exe \"five\"\"\"\"\"quotes next",
1252 {"exe", "five\"\"quotes", "next", NULL}, 0},
1253
1254 {"exe \"six\"\"\"\"\"\" quotes\" next 7%3=1",
1255 {"exe", "six\"\" quotes", "next", "7%3=1", NULL}, 0},
1256
1257 {"exe \"eleven\"\"\"\"\"\"\"\"\"\"\"quotes next",
1258 {"exe", "eleven\"\"\"\"quotes", "next", NULL}, 0},
1259
1260 {"exe \"twelve\"\"\"\"\"\"\"\"\"\"\"\" quotes\" next 13%3=1",
1261 {"exe", "twelve\"\"\"\" quotes", "next", "13%3=1", NULL}, 0},
1262
1263 /* Escaped consecutive quotes are fun */
1264 {"exe \"the crazy \\\\\"\"\"\\\\\" quotes",
1265 {"exe", "the crazy \\\"\\", "quotes", NULL}, 0},
1266
1267 /* The executable path has its own rules!!!
1268 * - Backslashes have no special meaning.
1269 * - If the first character is a quote, then the second quote ends the
1270 * executable path.
1271 * - The previous rule holds even if the next character is not a space!
1272 * - If the first character is not a quote, then quotes have no special
1273 * meaning either and the executable path stops at the first space.
1274 * - The consecutive quotes rules don't apply either.
1275 * - Even if there is no space between the executable path and the first
1276 * argument, the latter is parsed using the regular rules.
1277 */
1278 {"exe\"file\"path arg1",
1279 {"exe\"file\"path", "arg1", NULL}, 0},
1280
1281 {"exe\"file\"path\targ1",
1282 {"exe\"file\"path", "arg1", NULL}, 0},
1283
1284 {"exe\"path\\ arg1",
1285 {"exe\"path\\", "arg1", NULL}, 0},
1286
1287 {"\\\"exe \"arg one\"",
1288 {"\\\"exe", "arg one", NULL}, 0},
1289
1290 {"\"spaced exe\" \"next arg\"",
1291 {"spaced exe", "next arg", NULL}, 0},
1292
1293 {"\"spaced exe\"\t\"next arg\"",
1294 {"spaced exe", "next arg", NULL}, 0},
1295
1296 {"\"exe\"arg\" one\" argtwo",
1297 {"exe", "arg one", "argtwo", NULL}, 0},
1298
1299 {"\"spaced exe\\\"arg1 arg2",
1300 {"spaced exe\\", "arg1", "arg2", NULL}, 0},
1301
1302 {"\"two\"\" arg1 ",
1303 {"two", " arg1 ", NULL}, 0},
1304
1305 {"\"three\"\"\" arg2",
1306 {"three", "", "arg2", NULL}, 0},
1307
1308 {"\"four\"\"\"\"arg1",
1309 {"four", "\"arg1", NULL}, 0},
1310
1311 /* If the first character is a space then the executable path is empty */
1312 {" \"arg\"one argtwo",
1313 {"", "argone", "argtwo", NULL}, 0},
1314
1315 {NULL, {NULL}, 0}
1316};
1317
1319{
1320 WCHAR cmdW[MAX_PATH], argW[MAX_PATH];
1321 LPWSTR *cl2a;
1322 int cl2a_count;
1323 LPWSTR *argsW;
1324 int i, count;
1325
1326 /* trace("----- cmd='%s'\n", test->cmd); */
1327 MultiByteToWideChar(CP_ACP, 0, test->cmd, -1, cmdW, ARRAY_SIZE(cmdW));
1328 argsW = cl2a = CommandLineToArgvW(cmdW, &cl2a_count);
1329 if (argsW == NULL && cl2a_count == -1)
1330 {
1331 win_skip("CommandLineToArgvW not implemented, skipping\n");
1332 return FALSE;
1333 }
1334 ok(!argsW[cl2a_count] || broken(argsW[cl2a_count] != NULL) /* before Vista */,
1335 "expected NULL-terminated list of commandline arguments\n");
1336
1337 count = 0;
1338 while (test->args[count])
1339 count++;
1340 todo_wine_if(test->todo & 0x1)
1341 ok(cl2a_count == count, "%s: expected %d arguments, but got %d\n", test->cmd, count, cl2a_count);
1342
1343 for (i = 0; i < cl2a_count; i++)
1344 {
1345 if (i < count)
1346 {
1347 MultiByteToWideChar(CP_ACP, 0, test->args[i], -1, argW, ARRAY_SIZE(argW));
1348 todo_wine_if(test->todo & (1 << (i+4)))
1349 ok(!lstrcmpW(*argsW, argW), "%s: arg[%d] expected %s but got %s\n", test->cmd, i, wine_dbgstr_w(argW), wine_dbgstr_w(*argsW));
1350 }
1351 else todo_wine_if(test->todo & 0x1)
1352 ok(0, "%s: got extra arg[%d]=%s\n", test->cmd, i, wine_dbgstr_w(*argsW));
1353 argsW++;
1354 }
1355 LocalFree(cl2a);
1356 return TRUE;
1357}
1358
1359static void test_commandline2argv(void)
1360{
1361 static const WCHAR exeW[] = {'e','x','e',0};
1362 const cmdline_tests_t* test;
1364 LPWSTR *args;
1365 int numargs;
1366 DWORD le;
1367
1369 while (test->cmd)
1370 {
1371 if (!test_one_cmdline(test))
1372 return;
1373 test++;
1374 }
1375
1376 SetLastError(0xdeadbeef);
1377 args = CommandLineToArgvW(exeW, NULL);
1378 le = GetLastError();
1379 ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le);
1380
1381 SetLastError(0xdeadbeef);
1383 le = GetLastError();
1384 ok(args == NULL && le == ERROR_INVALID_PARAMETER, "expected NULL with ERROR_INVALID_PARAMETER got %p with %u\n", args, le);
1385
1386 *strW = 0;
1387 args = CommandLineToArgvW(strW, &numargs);
1388 ok(numargs == 1 || broken(numargs > 1), "expected 1 args, got %d\n", numargs);
1389 ok(!args || (!args[numargs] || broken(args[numargs] != NULL) /* before Vista */),
1390 "expected NULL-terminated list of commandline arguments\n");
1391 if (numargs == 1)
1392 {
1394 ok(!lstrcmpW(args[0], strW), "wrong path to the current executable: %s instead of %s\n", wine_dbgstr_w(args[0]), wine_dbgstr_w(strW));
1395 }
1396 if (args) LocalFree(args);
1397}
1398
1399/* The goal here is to analyze how ShellExecute() builds the command that
1400 * will be run. The tricky part is that there are three transformation
1401 * steps between the 'parameters' string we pass to ShellExecute() and the
1402 * argument list we observe in the child process:
1403 * - The parsing of 'parameters' string into individual arguments. The tests
1404 * show this is done differently from both CreateProcess() and
1405 * CommandLineToArgv()!
1406 * - The way the command 'formatting directives' such as %1, %2, etc are
1407 * handled.
1408 * - And the way the resulting command line is then parsed to yield the
1409 * argument list we check.
1410 */
1411typedef struct
1412{
1413 const char* verb;
1414 const char* params;
1415 int todo;
1416 const char *cmd;
1417 const char *broken;
1419
1421{
1422 /* Start with three simple parameters. Notice that one can reorder and
1423 * duplicate the parameters. Also notice how %* take the raw input
1424 * parameters string, including the trailing spaces, no matter what
1425 * arguments have already been used.
1426 */
1427 {"Params232S", "p2 p3 p4 ", TRUE,
1428 " p2 p3 \"p2\" \"p2 p3 p4 \""},
1429
1430 /* Unquoted argument references like %2 don't automatically quote their
1431 * argument. Similarly, when they are quoted they don't escape the quotes
1432 * that their argument may contain.
1433 */
1434 {"Params232S", "\"p two\" p3 p4 ", TRUE,
1435 " p two p3 \"p two\" \"\"p two\" p3 p4 \""},
1436
1437 /* Only single digits are supported so only %1 to %9. Shown here with %20
1438 * because %10 is a pain.
1439 */
1440 {"Params20", "p", FALSE,
1441 " \"p0\""},
1442
1443 /* Only (double-)quotes have a special meaning. */
1444 {"Params23456", "'p2 p3` p4\\ $even", FALSE,
1445 " \"'p2\" \"p3`\" \"p4\\\" \"$even\" \"\""},
1446
1447 {"Params23456", "p=2 p-3 p4\tp4\rp4\np4", TRUE,
1448 " \"p=2\" \"p-3\" \"p4\tp4\rp4\np4\" \"\" \"\""},
1449
1450 /* In unquoted strings, quotes are treated are a parameter separator just
1451 * like spaces! However they can be doubled to get a literal quote.
1452 * Specifically:
1453 * 2n quotes -> n quotes
1454 * 2n+1 quotes -> n quotes and a parameter separator
1455 */
1456 {"Params23456789", "one\"quote \"p four\" one\"quote p7", TRUE,
1457 " \"one\" \"quote\" \"p four\" \"one\" \"quote\" \"p7\" \"\" \"\""},
1458
1459 {"Params23456789", "two\"\"quotes \"p three\" two\"\"quotes p5", TRUE,
1460 " \"two\"quotes\" \"p three\" \"two\"quotes\" \"p5\" \"\" \"\" \"\" \"\""},
1461
1462 {"Params23456789", "three\"\"\"quotes \"p four\" three\"\"\"quotes p6", TRUE,
1463 " \"three\"\" \"quotes\" \"p four\" \"three\"\" \"quotes\" \"p6\" \"\" \"\""},
1464
1465 {"Params23456789", "four\"\"\"\"quotes \"p three\" four\"\"\"\"quotes p5", TRUE,
1466 " \"four\"\"quotes\" \"p three\" \"four\"\"quotes\" \"p5\" \"\" \"\" \"\" \"\""},
1467
1468 /* Quoted strings cannot be continued by tacking on a non space character
1469 * either.
1470 */
1471 {"Params23456", "\"p two\"p3 \"p four\"p5 p6", TRUE,
1472 " \"p two\" \"p3\" \"p four\" \"p5\" \"p6\""},
1473
1474 /* In quoted strings, the quotes are halved and an odd number closes the
1475 * string. Specifically:
1476 * 2n quotes -> n quotes
1477 * 2n+1 quotes -> n quotes and closes the string and hence the parameter
1478 */
1479 {"Params23456789", "\"one q\"uote \"p four\" \"one q\"uote p7", TRUE,
1480 " \"one q\" \"uote\" \"p four\" \"one q\" \"uote\" \"p7\" \"\" \"\""},
1481
1482 {"Params23456789", "\"two \"\" quotes\" \"p three\" \"two \"\" quotes\" p5", TRUE,
1483 " \"two \" quotes\" \"p three\" \"two \" quotes\" \"p5\" \"\" \"\" \"\" \"\""},
1484
1485 {"Params23456789", "\"three q\"\"\"uotes \"p four\" \"three q\"\"\"uotes p7", TRUE,
1486 " \"three q\"\" \"uotes\" \"p four\" \"three q\"\" \"uotes\" \"p7\" \"\" \"\""},
1487
1488 {"Params23456789", "\"four \"\"\"\" quotes\" \"p three\" \"four \"\"\"\" quotes\" p5", TRUE,
1489 " \"four \"\" quotes\" \"p three\" \"four \"\" quotes\" \"p5\" \"\" \"\" \"\" \"\""},
1490
1491 /* The quoted string rules also apply to consecutive quotes at the start
1492 * of a parameter but don't count the opening quote!
1493 */
1494 {"Params23456789", "\"\"twoquotes \"p four\" \"\"twoquotes p7", TRUE,
1495 " \"\" \"twoquotes\" \"p four\" \"\" \"twoquotes\" \"p7\" \"\" \"\""},
1496
1497 {"Params23456789", "\"\"\"three quotes\" \"p three\" \"\"\"three quotes\" p5", TRUE,
1498 " \"\"three quotes\" \"p three\" \"\"three quotes\" \"p5\" \"\" \"\" \"\" \"\""},
1499
1500 {"Params23456789", "\"\"\"\"fourquotes \"p four\" \"\"\"\"fourquotes p7", TRUE,
1501 " \"\"\" \"fourquotes\" \"p four\" \"\"\" \"fourquotes\" \"p7\" \"\" \"\""},
1502
1503 /* An unclosed quoted string gets lost! */
1504 {"Params23456", "p2 \"p3\" \"p4 is lost", TRUE,
1505 " \"p2\" \"p3\" \"\" \"\" \"\"",
1506 " \"p2\" \"p3\" \"p3\" \"\" \"\""}, /* NT4/2k */
1507
1508 /* Backslashes have no special meaning even when preceding quotes. All
1509 * they do is start an unquoted string.
1510 */
1511 {"Params23456", "\\\"p\\three \"pfour\\\" pfive", TRUE,
1512 " \"\\\" \"p\\three\" \"pfour\\\" \"pfive\" \"\""},
1513
1514 /* Environment variables are left untouched. */
1515 {"Params23456", "%TMPDIR% %t %c", FALSE,
1516 " \"%TMPDIR%\" \"%t\" \"%c\" \"\" \"\""},
1517
1518 /* %~2 is equivalent to %*. However %~3 and higher include the spaces
1519 * before the parameter!
1520 * (but not the previous parameter's closing quote fortunately)
1521 */
1522 {"Params2345Etc", "p2 p3 \"p4\" p5 p6 ", TRUE,
1523 " ~2=\"p2 p3 \"p4\" p5 p6 \" ~3=\" p3 \"p4\" p5 p6 \" ~4=\" \"p4\" p5 p6 \" ~5= p5 p6 "},
1524
1525 /* %~n works even if there is no nth parameter. */
1526 {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 ", TRUE,
1527 " ~9=\" \""},
1528
1529 {"Params9Etc", "p2 p3 p4 p5 p6 p7 ", TRUE,
1530 " ~9=\"\""},
1531
1532 /* The %~n directives also transmit the tenth parameter and beyond. */
1533 {"Params9Etc", "p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 and beyond!", TRUE,
1534 " ~9=\" p9 p10 p11 and beyond!\""},
1535
1536 /* Bad formatting directives lose their % sign, except those followed by
1537 * a tilde! Environment variables are not expanded but lose their % sign.
1538 */
1539 {"ParamsBad", "p2 p3 p4 p5", TRUE,
1540 " \"% - %~ %~0 %~1 %~a %~* a b c TMPDIR\""},
1541
1542 {0}
1543};
1544
1545static void test_argify(void)
1546{
1547 char fileA[MAX_PATH], params[2*MAX_PATH+12];
1548 INT_PTR rc;
1549 const argify_tests_t* test;
1550 const char *bad;
1551 const char* cmd;
1552
1553 /* Test with a long parameter */
1554 for (rc = 0; rc < MAX_PATH; rc++)
1555 fileA[rc] = 'a' + rc % 26;
1556 fileA[MAX_PATH-1] = '\0';
1557 sprintf(params, "shlexec \"%s\" %s", child_file, fileA);
1558
1559 /* We need NOZONECHECKS on Win2003 to block a dialog */
1561 okShell(rc > 32, "failed: rc=%lu\n", rc);
1562 okChildInt("argcA", 4);
1563 okChildPath("argvA3", fileA);
1564
1566 {
1567 skip("No argify tests due to lack of .shlexec association\n");
1568 return;
1569 }
1570
1571 create_test_verb("shlexec.shlexec", "Params232S", 0, "Params232S %2 %3 \"%2\" \"%*\"");
1572 create_test_verb("shlexec.shlexec", "Params23456", 0, "Params23456 \"%2\" \"%3\" \"%4\" \"%5\" \"%6\"");
1573 create_test_verb("shlexec.shlexec", "Params23456789", 0, "Params23456789 \"%2\" \"%3\" \"%4\" \"%5\" \"%6\" \"%7\" \"%8\" \"%9\"");
1574 create_test_verb("shlexec.shlexec", "Params2345Etc", 0, "Params2345Etc ~2=\"%~2\" ~3=\"%~3\" ~4=\"%~4\" ~5=%~5");
1575 create_test_verb("shlexec.shlexec", "Params9Etc", 0, "Params9Etc ~9=\"%~9\"");
1576 create_test_verb("shlexec.shlexec", "Params20", 0, "Params20 \"%20\"");
1577 create_test_verb("shlexec.shlexec", "ParamsBad", 0, "ParamsBad \"%% %- %~ %~0 %~1 %~a %~* %a %b %c %TMPDIR%\"");
1578
1579 sprintf(fileA, "%s\\test file.shlexec", tmpdir);
1580
1582 while (test->params)
1583 {
1584 bad = test->broken ? test->broken : test->cmd;
1585
1586 rc = shell_execute_ex(SEE_MASK_DOENVSUBST, test->verb, fileA, test->params, NULL, NULL);
1587 okShell(rc > 32, "failed: rc=%lu\n", rc);
1588
1589 cmd = getChildString("Child", "cmdlineA");
1590 /* Our commands are such that the verb immediately precedes the
1591 * part we are interested in.
1592 */
1593 if (cmd) cmd = strstr(cmd, test->verb);
1594 if (cmd) cmd += strlen(test->verb);
1595 if (!cmd) cmd = "(null)";
1596 todo_wine_if(test->todo)
1597 okShell(!strcmp(cmd, test->cmd) || broken(!strcmp(cmd, bad)),
1598 "expected '%s', got '%s'\n", cmd, test->cmd);
1599 test++;
1600 }
1601}
1602
1603static void test_filename(void)
1604{
1605 char filename[MAX_PATH];
1606 const filename_tests_t* test;
1607 char* c;
1608 INT_PTR rc;
1609
1611 {
1612 skip("No ShellExecute/filename tests due to lack of .shlexec association\n");
1613 return;
1614 }
1615
1617 while (test->basename)
1618 {
1619 BOOL quotedfile = FALSE;
1620
1622 {
1623 win_skip("Skipping shellexecute of file with unassociated extension\n");
1624 test++;
1625 continue;
1626 }
1627
1628 sprintf(filename, test->basename, tmpdir);
1629 if (strchr(filename, '/'))
1630 {
1631 c=filename;
1632 while (*c)
1633 {
1634 if (*c=='\\')
1635 *c='/';
1636 c++;
1637 }
1638 }
1639 if ((test->todo & 0x40)==0)
1640 {
1641 rc=shell_execute(test->verb, filename, NULL, NULL);
1642 }
1643 else
1644 {
1645 char quoted[MAX_PATH + 2];
1646
1647 quotedfile = TRUE;
1648 sprintf(quoted, "\"%s\"", filename);
1649 rc=shell_execute(test->verb, quoted, NULL, NULL);
1650 }
1651 if (rc > 32)
1652 rc=33;
1653 okShell(rc==test->rc ||
1654 broken(quotedfile && rc == SE_ERR_FNF), /* NT4 */
1655 "failed: rc=%ld err=%u\n", rc, GetLastError());
1656 if (rc == 33)
1657 {
1658 const char* verb;
1659 todo_wine_if(test->todo & 0x2)
1660 okChildInt("argcA", 5);
1661 verb=(test->verb ? test->verb : "Open");
1662 todo_wine_if(test->todo & 0x4)
1663 okChildString("argvA3", verb);
1664 todo_wine_if(test->todo & 0x8)
1665 okChildPath("argvA4", filename);
1666 }
1667 test++;
1668 }
1669
1671 while (test->basename)
1672 {
1673 sprintf(filename, test->basename, tmpdir);
1674 rc=shell_execute(test->verb, filename, NULL, NULL);
1675 if (rc > 32)
1676 rc=33;
1677 todo_wine_if(test->todo & 0x1)
1678 okShell(rc==test->rc, "failed: rc=%ld err=%u\n", rc, GetLastError());
1679 if (rc==0)
1680 {
1681 int count;
1682 const char* verb;
1683 char* str;
1684
1685 verb=(test->verb ? test->verb : "Open");
1686 todo_wine_if(test->todo & 0x4)
1687 okChildString("argvA3", verb);
1688
1689 count=4;
1690 str=filename;
1691 while (1)
1692 {
1693 char attrib[18];
1694 char* space;
1695 space=strchr(str, ' ');
1696 if (space)
1697 *space='\0';
1698 sprintf(attrib, "argvA%d", count);
1699 todo_wine_if(test->todo & 0x8)
1700 okChildPath(attrib, str);
1701 count++;
1702 if (!space)
1703 break;
1704 str=space+1;
1705 }
1706 todo_wine_if(test->todo & 0x2)
1707 okChildInt("argcA", count);
1708 }
1709 test++;
1710 }
1711
1712 if (dllver.dwMajorVersion != 0)
1713 {
1714 /* The more recent versions of shell32.dll accept quoted filenames
1715 * while older ones (e.g. 4.00) don't. Still we want to test this
1716 * because IE 6 depends on the new behavior.
1717 * One day we may need to check the exact version of the dll but for
1718 * now making sure DllGetVersion() is present is sufficient.
1719 */
1720 sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
1722 okShell(rc > 32, "failed: rc=%ld err=%u\n", rc, GetLastError());
1723 okChildInt("argcA", 5);
1724 okChildString("argvA3", "Open");
1725 sprintf(filename, "%s\\test file.shlexec", tmpdir);
1726 okChildPath("argvA4", filename);
1727 }
1728
1729 sprintf(filename, "\"%s\\test file.sha\"", tmpdir);
1731 todo_wine okShell(rc > 32, "failed: rc=%ld err=%u\n", rc, GetLastError());
1732 okChildInt("argcA", 5);
1733 todo_wine okChildString("argvA3", "averb");
1734 sprintf(filename, "%s\\test file.sha", tmpdir);
1735 todo_wine okChildPath("argvA4", filename);
1736}
1737
1738typedef struct
1739{
1740 const char* urlprefix;
1741 const char* basename;
1743 int todo;
1745
1746#define URL_SUCCESS 0x1
1747#define USE_COLON 0x2
1748#define USE_BSLASH 0x4
1749
1751{
1752 /* How many slashes does it take... */
1753 {"file:", "%s\\test file.shlexec", URL_SUCCESS, 0},
1754 {"file:/", "%s\\test file.shlexec", URL_SUCCESS, 0},
1755 {"file://", "%s\\test file.shlexec", URL_SUCCESS, 0},
1756 {"file:///", "%s\\test file.shlexec", URL_SUCCESS, 0},
1757 {"File:///", "%s\\test file.shlexec", URL_SUCCESS, 0},
1758 {"file:////", "%s\\test file.shlexec", URL_SUCCESS, 0},
1759 {"file://///", "%s\\test file.shlexec", 0, 0},
1760
1761 /* Test with Windows-style paths */
1762 {"file:///", "%s\\test file.shlexec", URL_SUCCESS | USE_COLON, 0},
1763 {"file:///", "%s\\test file.shlexec", URL_SUCCESS | USE_BSLASH, 0},
1764
1765 /* Check handling of hostnames */
1766 {"file://localhost/", "%s\\test file.shlexec", URL_SUCCESS, 0},
1767 {"file://localhost:80/", "%s\\test file.shlexec", 0, 0},
1768 {"file://LocalHost/", "%s\\test file.shlexec", URL_SUCCESS, 0},
1769 {"file://127.0.0.1/", "%s\\test file.shlexec", 0, 0},
1770 {"file://::1/", "%s\\test file.shlexec", 0, 0},
1771 {"file://notahost/", "%s\\test file.shlexec", 0, 0},
1772
1773 /* Environment variables are not expanded in URLs */
1774 {"%urlprefix%", "%s\\test file.shlexec", 0, 0x1},
1775 {"file:///", "%%TMPDIR%%\\test file.shlexec", 0, 0},
1776
1777 /* Test shortcuts vs. URLs */
1778 {"file://///", "%s\\test_shortcut_shlexec.lnk", 0, 0x1c},
1779
1780 /* Confuse things by mixing protocols */
1781 {"file://", "shlproto://foo/bar", USE_COLON, 0},
1782
1783 {NULL, NULL, 0, 0}
1784};
1785
1786static void test_fileurls(void)
1787{
1788 char filename[MAX_PATH], fileurl[MAX_PATH], longtmpdir[MAX_PATH];
1789 char command[MAX_PATH];
1790 const fileurl_tests_t* test;
1791 char *s;
1792 INT_PTR rc;
1793
1795 {
1796 skip("No file URL tests due to lack of .shlexec association\n");
1797 return;
1798 }
1799
1801 "file:///nosuchfile.shlexec", NULL, NULL, NULL);
1802 if (rc > 32)
1803 {
1804 win_skip("shell32 is too old (likely < 4.72). Skipping the file URL tests\n");
1805 return;
1806 }
1807
1808 get_long_path_name(tmpdir, longtmpdir, ARRAY_SIZE(longtmpdir));
1809 SetEnvironmentVariableA("urlprefix", "file:///");
1810
1812 while (test->basename)
1813 {
1814 /* Build the file URL */
1815 sprintf(filename, test->basename, longtmpdir);
1816 strcpy(fileurl, test->urlprefix);
1817 strcat(fileurl, filename);
1818 s = fileurl + strlen(test->urlprefix);
1819 while (*s)
1820 {
1821 if (!(test->flags & USE_COLON) && *s == ':')
1822 *s = '|';
1823 else if (!(test->flags & USE_BSLASH) && *s == '\\')
1824 *s = '/';
1825 s++;
1826 }
1827
1828 /* Test it first with FindExecutable() */
1829 rc = (INT_PTR)FindExecutableA(fileurl, NULL, command);
1830 ok(rc == SE_ERR_FNF, "FindExecutable(%s) failed: bad rc=%lu\n", fileurl, rc);
1831
1832 /* Then ShellExecute() */
1833 if ((test->todo & 0x10) == 0)
1834 rc = shell_execute(NULL, fileurl, NULL, NULL);
1835 else todo_wait
1836 rc = shell_execute(NULL, fileurl, NULL, NULL);
1837 if (bad_shellexecute)
1838 {
1839 win_skip("shell32 is too old (likely 4.72). Skipping the file URL tests\n");
1840 break;
1841 }
1842 if (test->flags & URL_SUCCESS)
1843 {
1844 todo_wine_if(test->todo & 0x1)
1845 okShell(rc > 32, "failed: bad rc=%lu\n", rc);
1846 }
1847 else
1848 {
1849 todo_wine_if(test->todo & 0x1)
1850 okShell(rc == SE_ERR_FNF || rc == SE_ERR_PNF ||
1851 broken(rc == SE_ERR_ACCESSDENIED) /* win2000 */,
1852 "failed: bad rc=%lu\n", rc);
1853 }
1854 if (rc == 33)
1855 {
1856 todo_wine_if(test->todo & 0x2)
1857 okChildInt("argcA", 5);
1858 todo_wine_if(test->todo & 0x4)
1859 okChildString("argvA3", "Open");
1860 todo_wine_if(test->todo & 0x8)
1861 okChildPath("argvA4", filename);
1862 }
1863 test++;
1864 }
1865
1866 SetEnvironmentVariableA("urlprefix", NULL);
1867}
1868
1869static void test_urls(void)
1870{
1871 char url[MAX_PATH];
1872 INT_PTR rc;
1873
1874 if (!create_test_class("fakeproto", FALSE))
1875 {
1876 skip("Unable to create 'fakeproto' class for URL tests\n");
1877 return;
1878 }
1879 create_test_verb("fakeproto", "open", 0, "URL %1");
1880
1881 create_test_class("shlpaverb", TRUE);
1882 create_test_verb("shlpaverb", "averb", 0, "PAVerb \"%1\"");
1883
1884 /* Protocols must be properly declared */
1885 rc = shell_execute(NULL, "notaproto://foo", NULL, NULL);
1887 "%s returned %lu\n", shell_call, rc);
1888
1889 rc = shell_execute(NULL, "fakeproto://foo/bar", NULL, NULL);
1891 "%s returned %lu\n", shell_call, rc);
1892
1893 /* Here's a real live one */
1894 rc = shell_execute(NULL, "shlproto://foo/bar", NULL, NULL);
1895 ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc);
1896 okChildInt("argcA", 5);
1897 okChildString("argvA3", "URL");
1898 okChildString("argvA4", "shlproto://foo/bar");
1899
1900 /* Check default verb detection */
1901 rc = shell_execute(NULL, "shlpaverb://foo/bar", NULL, NULL);
1902 todo_wine ok(rc > 32 || /* XP+IE7 - Win10 */
1903 broken(rc == SE_ERR_NOASSOC), /* XP+IE6 */
1904 "%s failed: rc=%lu\n", shell_call, rc);
1905 if (rc > 32)
1906 {
1907 okChildInt("argcA", 5);
1908 todo_wine okChildString("argvA3", "PAVerb");
1909 todo_wine okChildString("argvA4", "shlpaverb://foo/bar");
1910 }
1911
1912 /* But alternative verbs are a recent feature! */
1913 rc = shell_execute("averb", "shlproto://foo/bar", NULL, NULL);
1914 ok(rc > 32 || /* Win8 - Win10 */
1915 broken(rc == SE_ERR_ACCESSDENIED), /* XP - Win7 */
1916 "%s failed: rc=%lu\n", shell_call, rc);
1917 if (rc > 32)
1918 {
1919 okChildString("argvA3", "AVerb");
1920 okChildString("argvA4", "shlproto://foo/bar");
1921 }
1922
1923 /* A .lnk ending does not turn a URL into a shortcut */
1924 rc = shell_execute(NULL, "shlproto://foo/bar.lnk", NULL, NULL);
1925 ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc);
1926 okChildInt("argcA", 5);
1927 okChildString("argvA3", "URL");
1928 okChildString("argvA4", "shlproto://foo/bar.lnk");
1929
1930 /* Neither does a .exe extension */
1931 rc = shell_execute(NULL, "shlproto://foo/bar.exe", NULL, NULL);
1932 ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc);
1933 okChildInt("argcA", 5);
1934 okChildString("argvA3", "URL");
1935 okChildString("argvA4", "shlproto://foo/bar.exe");
1936
1937 /* But a class name overrides it */
1938 rc = shell_execute(NULL, "shlproto://foo/bar", "shlexec.shlexec", NULL);
1939 ok(rc > 32, "%s failed: rc=%lu\n", shell_call, rc);
1940 okChildInt("argcA", 5);
1941 okChildString("argvA3", "URL");
1942 okChildString("argvA4", "shlproto://foo/bar");
1943
1944 /* Environment variables are expanded in URLs (but not in file URLs!) */
1946 NULL, "shlproto://%TMPDIR%/bar", NULL, NULL, NULL);
1947 okShell(rc > 32, "failed: rc=%lu\n", rc);
1948 okChildInt("argcA", 5);
1949 sprintf(url, "shlproto://%s/bar", tmpdir);
1950 okChildString("argvA3", "URL");
1951 okChildStringBroken("argvA4", url, "shlproto://%TMPDIR%/bar");
1952
1953 /* But only after the path has been identified as a URL */
1954 SetEnvironmentVariableA("urlprefix", "shlproto:///");
1955 rc = shell_execute(NULL, "%urlprefix%foo", NULL, NULL);
1956 todo_wine ok(rc == SE_ERR_FNF, "%s returned %lu\n", shell_call, rc);
1957 SetEnvironmentVariableA("urlprefix", NULL);
1958
1959 delete_test_class("fakeproto");
1960 delete_test_class("shlpaverb");
1961}
1962
1963static void test_find_executable(void)
1964{
1965 char notepad_path[MAX_PATH];
1966 char filename[MAX_PATH];
1967 char command[MAX_PATH];
1968 const filename_tests_t* test;
1969 INT_PTR rc;
1970
1971 if (!create_test_association(".sfe"))
1972 {
1973 skip("Unable to create association for '.sfe'\n");
1974 return;
1975 }
1976 create_test_verb("shlexec.sfe", "Open", 1, "%1");
1977
1978 /* Don't test FindExecutable(..., NULL), it always crashes */
1979
1980 strcpy(command, "your word");
1981 if (0) /* Can crash on Vista! */
1982 {
1984 ok(rc == SE_ERR_FNF || rc > 32 /* nt4 */, "FindExecutable(NULL) returned %ld\n", rc);
1985 ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
1986 }
1987
1988 GetSystemDirectoryA( notepad_path, MAX_PATH );
1989 strcat( notepad_path, "\\notepad.exe" );
1990
1991 /* Search for something that should be in the system-wide search path (no default directory) */
1992 strcpy(command, "your word");
1993 rc=(INT_PTR)FindExecutableA("notepad.exe", NULL, command);
1994 ok(rc > 32, "FindExecutable(%s) returned %ld\n", "notepad.exe", rc);
1995 ok(strcasecmp(command, notepad_path) == 0, "FindExecutable(%s) returned command=[%s]\n", "notepad.exe", command);
1996
1997 /* Search for something that should be in the system-wide search path (with default directory) */
1998 strcpy(command, "your word");
1999 rc=(INT_PTR)FindExecutableA("notepad.exe", tmpdir, command);
2000 ok(rc > 32, "FindExecutable(%s) returned %ld\n", "notepad.exe", rc);
2001 ok(strcasecmp(command, notepad_path) == 0, "FindExecutable(%s) returned command=[%s]\n", "notepad.exe", command);
2002
2003 strcpy(command, "your word");
2005 ok(rc == SE_ERR_NOASSOC /* >= win2000 */ || rc > 32 /* win98, nt4 */, "FindExecutable(NULL) returned %ld\n", rc);
2006 ok(strcmp(command, "your word") != 0, "FindExecutable(NULL) returned command=[%s]\n", command);
2007
2008 sprintf(filename, "%s\\test file.sfe", tmpdir);
2010 ok(rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
2011 /* Depending on the platform, command could be '%1' or 'test file.sfe' */
2012
2013 rc=(INT_PTR)FindExecutableA("test file.sfe", tmpdir, command);
2014 ok(rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
2015
2016 rc=(INT_PTR)FindExecutableA("test file.sfe", NULL, command);
2017 ok(rc == SE_ERR_FNF, "FindExecutable(%s) returned %ld\n", filename, rc);
2018
2020
2021 if (!create_test_association(".shl"))
2022 {
2023 skip("Unable to create association for '.shl'\n");
2024 return;
2025 }
2026 create_test_verb("shlexec.shl", "Open", 0, "Open");
2027
2028 sprintf(filename, "%s\\test file.shl", tmpdir);
2030 ok(rc == SE_ERR_FNF /* NT4 */ || rc > 32, "FindExecutable(%s) returned %ld\n", filename, rc);
2031
2032 sprintf(filename, "%s\\test file.shlfoo", tmpdir);
2034
2036
2037 if (rc > 32)
2038 {
2039 /* On Windows XP and 2003 FindExecutable() is completely broken.
2040 * Probably what it does is convert the filename to 8.3 format,
2041 * which as a side effect converts the '.shlfoo' extension to '.shl',
2042 * and then tries to find an association for '.shl'. This means it
2043 * will normally fail on most extensions with more than 3 characters,
2044 * like '.mpeg', etc.
2045 * Also it means we cannot do any other test.
2046 */
2047 win_skip("FindExecutable() is broken -> not running 4+ character extension tests\n");
2048 return;
2049 }
2050
2052 {
2053 skip("No FindExecutable/filename tests due to lack of .shlexec association\n");
2054 return;
2055 }
2056
2058 while (test->basename)
2059 {
2060 sprintf(filename, test->basename, tmpdir);
2061 if (strchr(filename, '/'))
2062 {
2063 char* c;
2064 c=filename;
2065 while (*c)
2066 {
2067 if (*c=='\\')
2068 *c='/';
2069 c++;
2070 }
2071 }
2072 /* Win98 does not '\0'-terminate command! */
2073 memset(command, '\0', sizeof(command));
2075 if (rc > 32)
2076 rc=33;
2077 todo_wine_if(test->todo & 0x10)
2078 ok(rc==test->rc, "FindExecutable(%s) failed: rc=%ld\n", filename, rc);
2079 if (rc > 32)
2080 {
2081 BOOL equal;
2082 equal=strcmp(command, argv0) == 0 ||
2083 /* NT4 returns an extra 0x8 character! */
2084 (strlen(command) == strlen(argv0)+1 && strncmp(command, argv0, strlen(argv0)) == 0);
2085 todo_wine_if(test->todo & 0x20)
2086 ok(equal, "FindExecutable(%s) returned command='%s' instead of '%s'\n",
2088 }
2089 test++;
2090 }
2091}
2092
2093
2095{
2096 /* Pass bad / nonexistent filenames as a parameter */
2097 {NULL, "%s\\nonexistent.shlexec", 0xa, 33},
2098 {NULL, "%s\\nonexistent.noassoc", 0xa, 33},
2099
2100 /* Pass regular paths as a parameter */
2101 {NULL, "%s\\test file.shlexec", 0xa, 33},
2102 {NULL, "%s/%%nasty%% $file.shlexec", 0xa, 33},
2103
2104 /* Pass filenames with no association as a parameter */
2105 {NULL, "%s\\test file.noassoc", 0xa, 33},
2106
2107 {NULL, NULL, 0}
2108};
2109
2110static void test_lnks(void)
2111{
2112 char filename[MAX_PATH];
2113 char params[MAX_PATH];
2114 const filename_tests_t* test;
2115 INT_PTR rc;
2116
2118 skip("No FindExecutable/filename tests due to lack of .shlexec association\n");
2119 else
2120 {
2121 /* Should open through our association */
2122 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
2124 okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError());
2125 okChildInt("argcA", 5);
2126 okChildString("argvA3", "Open");
2127 sprintf(params, "%s\\test file.shlexec", tmpdir);
2129 okChildPath("argvA4", filename);
2130
2131 rc=shell_execute_ex(SEE_MASK_NOZONECHECKS|SEE_MASK_DOENVSUBST, NULL, "%TMPDIR%\\test_shortcut_shlexec.lnk", NULL, NULL, NULL);
2132 okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError());
2133 okChildInt("argcA", 5);
2134 okChildString("argvA3", "Open");
2135 sprintf(params, "%s\\test file.shlexec", tmpdir);
2137 okChildPath("argvA4", filename);
2138 }
2139
2140 /* Should just run our executable */
2141 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
2143 okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError());
2144 okChildInt("argcA", 4);
2145 okChildString("argvA3", "Lnk");
2146
2147 if (!skip_shlexec_tests)
2148 {
2149 /* An explicit class overrides lnk's ContextMenuHandler */
2151 okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError());
2152 okChildInt("argcA", 5);
2153 okChildString("argvA3", "Open");
2154 okChildPath("argvA4", filename);
2155 }
2156
2157 if (dllver.dwMajorVersion>=6)
2158 {
2159 char* c;
2160 /* Recent versions of shell32.dll accept '/'s in shortcut paths.
2161 * Older versions don't or are quite buggy in this regard.
2162 */
2163 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
2164 c=filename;
2165 while (*c)
2166 {
2167 if (*c=='\\')
2168 *c='/';
2169 c++;
2170 }
2172 okShell(rc > 32, "failed: rc=%lu err=%u\n", rc, GetLastError());
2173 okChildInt("argcA", 4);
2174 okChildString("argvA3", "Lnk");
2175 }
2176
2177 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
2179 while (test->basename)
2180 {
2181 params[0]='\"';
2182 sprintf(params+1, test->basename, tmpdir);
2183 strcat(params,"\"");
2185 NULL, NULL);
2186 if (rc > 32)
2187 rc=33;
2188 todo_wine_if(test->todo & 0x1)
2189 okShell(rc==test->rc, "failed: rc=%lu err=%u\n", rc, GetLastError());
2190 if (rc==0)
2191 {
2192 todo_wine_if(test->todo & 0x2)
2193 okChildInt("argcA", 5);
2194 todo_wine_if(test->todo & 0x4)
2195 okChildString("argvA3", "Lnk");
2196 sprintf(params, test->basename, tmpdir);
2197 okChildPath("argvA4", params);
2198 }
2199 test++;
2200 }
2201}
2202
2203
2204static void test_exes(void)
2205{
2206 char filename[MAX_PATH];
2207 char params[1024];
2208 INT_PTR rc;
2209
2210 sprintf(params, "shlexec \"%s\" Exec", child_file);
2211
2212 /* We need NOZONECHECKS on Win2003 to block a dialog */
2214 NULL, NULL);
2215 okShell(rc > 32, "returned %lu\n", rc);
2216 okChildInt("argcA", 4);
2217 okChildString("argvA3", "Exec");
2218
2219 if (! skip_noassoc_tests)
2220 {
2221 sprintf(filename, "%s\\test file.noassoc", tmpdir);
2223 {
2225 todo_wine {
2226 okShell(rc==SE_ERR_NOASSOC, "returned %lu\n", rc);
2227 }
2228 }
2229 }
2230 else
2231 {
2232 win_skip("Skipping shellexecute of file with unassociated extension\n");
2233 }
2234
2235 /* test combining executable and parameters */
2236 sprintf(filename, "%s shlexec \"%s\" Exec", argv0, child_file);
2238 okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
2239
2240 sprintf(filename, "\"%s\" shlexec \"%s\" Exec", argv0, child_file);
2242 okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
2243
2244 /* A verb, even if invalid, overrides the normal handling of executables */
2246 "notaverb", argv0, NULL, NULL, NULL);
2247 todo_wine okShell(rc == SE_ERR_NOASSOC, "returned %lu\n", rc);
2248
2249 if (!skip_shlexec_tests)
2250 {
2251 /* A class overrides the normal handling of executables too */
2252 /* FIXME SEE_MASK_FLAG_NO_UI is only needed due to Wine's bug */
2254 NULL, argv0, NULL, NULL, ".shlexec");
2255 todo_wine okShell(rc > 32, "returned %lu\n", rc);
2256 okChildInt("argcA", 5);
2257 todo_wine okChildString("argvA3", "Open");
2258 todo_wine okChildPath("argvA4", argv0);
2259 }
2260}
2261
2262typedef struct
2263{
2264 const char* command;
2265 const char* ddeexec;
2266 const char* application;
2267 const char* topic;
2268 const char* ifexec;
2270 const char* expectedDdeExec;
2272} dde_tests_t;
2273
2275{
2276 /* Test passing and not passing command-line
2277 * argument, no DDE */
2278 {"", NULL, NULL, NULL, NULL, 0, ""},
2279 {"\"%1\"", NULL, NULL, NULL, NULL, 1, ""},
2280
2281 /* Test passing and not passing command-line
2282 * argument, with DDE */
2283 {"", "[open(\"%1\")]", "shlexec", "dde", NULL, 0, "[open(\"%s\")]"},
2284 {"\"%1\"", "[open(\"%1\")]", "shlexec", "dde", NULL, 1, "[open(\"%s\")]"},
2285
2286 /* Test unquoted %1 in command and ddeexec
2287 * (test filename has space) */
2288 {"%1", "[open(%1)]", "shlexec", "dde", NULL, 2, "[open(%s)]", TRUE /* before vista */},
2289
2290 /* Test ifexec precedence over ddeexec */
2291 {"", "[open(\"%1\")]", "shlexec", "dde", "[ifexec(\"%1\")]", 0, "[ifexec(\"%s\")]"},
2292
2293 /* Test default DDE topic */
2294 {"", "[open(\"%1\")]", "shlexec", NULL, NULL, 0, "[open(\"%s\")]"},
2295
2296 /* Test default DDE application */
2297 {"", "[open(\"%1\")]", NULL, "dde", NULL, 0, "[open(\"%s\")]"},
2298
2299 {NULL}
2300};
2301
2304{
2306 if (winetest_debug > 1)
2307 trace("WaitForInputIdle() waiting for dde event timeout=min(%u,5s)\n", timeout);
2308 timeout = timeout < 5000 ? timeout : 5000;
2310}
2311
2312/*
2313 * WaitForInputIdle() will normally return immediately for console apps. That's
2314 * a problem for us because ShellExecute will assume that an app is ready to
2315 * receive DDE messages after it has called WaitForInputIdle() on that app.
2316 * To work around that we install our own version of WaitForInputIdle() that
2317 * will wait for the child to explicitly tell us that it is ready. We do that
2318 * by changing the entry for WaitForInputIdle() in the shell32 import address
2319 * table.
2320 */
2322{
2323 char *base;
2324 PIMAGE_NT_HEADERS nt_headers;
2325 DWORD import_directory_rva;
2326 PIMAGE_IMPORT_DESCRIPTOR import_descriptor;
2327 int hook_count = 0;
2328
2329 base = (char *) GetModuleHandleA("shell32.dll");
2330 nt_headers = (PIMAGE_NT_HEADERS)(base + ((PIMAGE_DOS_HEADER) base)->e_lfanew);
2331 import_directory_rva = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
2332
2333 /* Search for the correct imported module by walking the import descriptors */
2334 import_descriptor = (PIMAGE_IMPORT_DESCRIPTOR)(base + import_directory_rva);
2335 while (U(*import_descriptor).OriginalFirstThunk != 0)
2336 {
2337 char *import_module_name;
2338
2339 import_module_name = base + import_descriptor->Name;
2340 if (lstrcmpiA(import_module_name, "user32.dll") == 0 ||
2341 lstrcmpiA(import_module_name, "user32") == 0)
2342 {
2343 PIMAGE_THUNK_DATA int_entry;
2344 PIMAGE_THUNK_DATA iat_entry;
2345
2346 /* The import name table and import address table are two parallel
2347 * arrays. We need the import name table to find the imported
2348 * routine and the import address table to patch the address, so
2349 * walk them side by side */
2350 int_entry = (PIMAGE_THUNK_DATA)(base + U(*import_descriptor).OriginalFirstThunk);
2351 iat_entry = (PIMAGE_THUNK_DATA)(base + import_descriptor->FirstThunk);
2352 while (int_entry->u1.Ordinal != 0)
2353 {
2354 if (! IMAGE_SNAP_BY_ORDINAL(int_entry->u1.Ordinal))
2355 {
2356 PIMAGE_IMPORT_BY_NAME import_by_name;
2357 import_by_name = (PIMAGE_IMPORT_BY_NAME)(base + int_entry->u1.AddressOfData);
2358 if (lstrcmpA((char *) import_by_name->Name, "WaitForInputIdle") == 0)
2359 {
2360 /* Found the correct routine in the correct imported module. Patch it. */
2361 DWORD old_prot;
2362 VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), PAGE_READWRITE, &old_prot);
2363 iat_entry->u1.Function = (ULONG_PTR) new_func;
2364 VirtualProtect(&iat_entry->u1.Function, sizeof(ULONG_PTR), old_prot, &old_prot);
2365 if (winetest_debug > 1)
2366 trace("Hooked %s.WaitForInputIdle\n", import_module_name);
2367 hook_count++;
2368 break;
2369 }
2370 }
2371 int_entry++;
2372 iat_entry++;
2373 }
2374 break;
2375 }
2376
2377 import_descriptor++;
2378 }
2379 ok(hook_count, "Could not hook WaitForInputIdle()\n");
2380}
2381
2382static void test_dde(void)
2383{
2384 char filename[MAX_PATH], defApplication[MAX_PATH];
2385 const dde_tests_t* test;
2386 char params[1024];
2387 INT_PTR rc;
2388 HANDLE map;
2389 char *shared_block;
2390 DWORD ddeflags;
2391
2393
2394 sprintf(filename, "%s\\test file.sde", tmpdir);
2395
2396 /* Default service is application name minus path and extension */
2397 strcpy(defApplication, strrchr(argv0, '\\')+1);
2398 *strchr(defApplication, '.') = 0;
2399
2401 4096, "winetest_shlexec_dde_map");
2402 shared_block = MapViewOfFile(map, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 4096);
2403
2405 test = dde_tests;
2406 while (test->command)
2407 {
2408 if (!create_test_association(".sde"))
2409 {
2410 skip("Unable to create association for '.sde'\n");
2411 return;
2412 }
2413 create_test_verb_dde("shlexec.sde", "Open", 0, test->command, test->ddeexec,
2414 test->application, test->topic, test->ifexec);
2415
2416 if (test->application != NULL || test->topic != NULL)
2417 {
2418 strcpy(shared_block, test->application ? test->application : defApplication);
2419 strcpy(shared_block + strlen(shared_block) + 1, test->topic ? test->topic : SZDDESYS_TOPIC);
2420 }
2421 else
2422 {
2423 shared_block[0] = '\0';
2424 shared_block[1] = '\0';
2425 }
2426 ddeExec[0] = 0;
2427
2429 dde_ready_event = CreateEventA(NULL, TRUE, FALSE, "winetest_shlexec_dde_ready");
2430 rc = shell_execute_ex(ddeflags, NULL, filename, NULL, NULL, NULL);
2432 if (!(ddeflags & SEE_MASK_WAITFORINPUTIDLE) && rc == SE_ERR_DDEFAIL &&
2434 strcmp(winetest_platform, "windows") == 0)
2435 {
2436 /* Windows 10 does not call WaitForInputIdle() for DDE which creates
2437 * a race condition as the DDE server may not have time to start up.
2438 * When that happens the test fails with the above results and we
2439 * compensate by forcing the WaitForInputIdle() call.
2440 */
2441 trace("Adding SEE_MASK_WAITFORINPUTIDLE for Windows 10\n");
2442 ddeflags |= SEE_MASK_WAITFORINPUTIDLE;
2445 continue;
2446 }
2447 okShell(32 < rc, "failed: rc=%lu err=%u\n", rc, GetLastError());
2448 if (test->ddeexec)
2450 broken(waitforinputidle_count == 0) /* Win10 race */,
2451 "WaitForInputIdle() was called %u times\n",
2453 else
2454 okShell(waitforinputidle_count == 0, "WaitForInputIdle() was called %u times for a non-DDE case\n", waitforinputidle_count);
2455
2456 if (32 < rc)
2457 {
2458 if (test->broken)
2459 okChildIntBroken("argcA", test->expectedArgs + 3);
2460 else
2461 okChildInt("argcA", test->expectedArgs + 3);
2462
2463 if (test->expectedArgs == 1) okChildPath("argvA3", filename);
2464
2465 sprintf(params, test->expectedDdeExec, filename);
2466 okChildPath("ddeExec", params);
2467 }
2469
2471 test++;
2472 }
2473
2474 UnmapViewOfFile(shared_block);
2477}
2478
2479#define DDE_DEFAULT_APP_VARIANTS 3
2480typedef struct
2481{
2482 const char* command;
2483 const char* expectedDdeApplication[DDE_DEFAULT_APP_VARIANTS];
2484 int todo;
2487
2489{
2490 /* There are three possible sets of results: Windows <= 2000, XP SP1 and
2491 * >= XP SP2. Use the first two tests to determine which results to expect.
2492 */
2493
2494 /* Test unquoted existing filename with a space */
2495 {"%s\\test file.exe", {"test file", "test file", "test"}, 0x0, {33, 33, 33}},
2496 {"%s\\test2 file.exe", {"test2", "", "test2"}, 0x0, {33, 5, 33}},
2497
2498 /* Test unquoted existing filename with a space */
2499 {"%s\\test file.exe param", {"test file", "test file", "test"}, 0x0, {33, 33, 33}},
2500
2501 /* Test quoted existing filename with a space */
2502 {"\"%s\\test file.exe\"", {"test file", "test file", "test file"}, 0x0, {33, 33, 33}},
2503 {"\"%s\\test file.exe\" param", {"test file", "test file", "test file"}, 0x0, {33, 33, 33}},
2504
2505 /* Test unquoted filename with a space that doesn't exist, but
2506 * test2.exe does */
2507 {"%s\\test2 file.exe param", {"test2", "", "test2"}, 0x0, {33, 5, 33}},
2508
2509 /* Test quoted filename with a space that does not exist */
2510 {"\"%s\\test2 file.exe\"", {"", "", "test2 file"}, 0x0, {5, 2, 33}},
2511 {"\"%s\\test2 file.exe\" param", {"", "", "test2 file"}, 0x0, {5, 2, 33}},
2512
2513 /* Test filename supplied without the extension */
2514 {"%s\\test2", {"test2", "", "test2"}, 0x0, {33, 5, 33}},
2515 {"%s\\test2 param", {"test2", "", "test2"}, 0x0, {33, 5, 33}},
2516
2517 /* Test an unquoted nonexistent filename */
2518 {"%s\\notexist.exe", {"", "", "notexist"}, 0x0, {5, 2, 33}},
2519 {"%s\\notexist.exe param", {"", "", "notexist"}, 0x0, {5, 2, 33}},
2520
2521 /* Test an application that will be found on the path */
2522 {"cmd", {"cmd", "cmd", "cmd"}, 0x0, {33, 33, 33}},
2523 {"cmd param", {"cmd", "cmd", "cmd"}, 0x0, {33, 33, 33}},
2524
2525 /* Test an application that will not be found on the path */
2526 {"xyzwxyzwxyz", {"", "", "xyzwxyzwxyz"}, 0x0, {5, 2, 33}},
2527 {"xyzwxyzwxyz param", {"", "", "xyzwxyzwxyz"}, 0x0, {5, 2, 33}},
2528
2529 {NULL, {NULL}, 0, {0}}
2530};
2531
2532typedef struct
2533{
2537
2539{
2541 assert(info && info->filename);
2542 PostThreadMessageA(info->threadIdParent,
2543 WM_QUIT,
2545 0);
2546 ExitThread(0);
2547}
2548
2549static void test_dde_default_app(void)
2550{
2551 char filename[MAX_PATH];
2552 HSZ hszApplication;
2555 char params[1024];
2556 DWORD threadId;
2557 MSG msg;
2558 INT_PTR rc;
2559 int which = 0;
2560 HDDEDATA ret;
2561 BOOL b;
2562
2564 ddeInst = 0;
2567 ok(rc == DMLERR_NO_ERROR, "got %lx\n", rc);
2568
2569 sprintf(filename, "%s\\test file.sde", tmpdir);
2570
2571 /* It is strictly not necessary to register an application name here, but wine's
2572 * DdeNameService implementation complains if 0 is passed instead of
2573 * hszApplication with DNS_FILTEROFF */
2574 hszApplication = DdeCreateStringHandleA(ddeInst, "shlexec", CP_WINANSI);
2576 ok(hszApplication && hszTopic, "got %p and %p\n", hszApplication, hszTopic);
2577 ret = DdeNameService(ddeInst, hszApplication, 0, DNS_REGISTER | DNS_FILTEROFF);
2578 ok(ret != 0, "got %p\n", ret);
2579
2581 while (test->command)
2582 {
2583 HANDLE thread;
2584
2585 if (!create_test_association(".sde"))
2586 {
2587 skip("Unable to create association for '.sde'\n");
2588 return;
2589 }
2590 sprintf(params, test->command, tmpdir);
2591 create_test_verb_dde("shlexec.sde", "Open", 1, params, "[test]", NULL,
2592 "shlexec", NULL);
2593 ddeApplication[0] = 0;
2594
2595 /* No application will be run as we will respond to the first DDE event,
2596 * so don't wait for it */
2598
2599 thread = CreateThread(NULL, 0, ddeThread, &info, 0, &threadId);
2600 ok(thread != NULL, "got %p\n", thread);
2601 while (GetMessageA(&msg, NULL, 0, 0)) DispatchMessageA(&msg);
2602 rc = msg.wParam > 32 ? 33 : msg.wParam;
2603
2604 /* The first two tests determine which set of results to expect.
2605 * First check the platform as only the first set of results is
2606 * acceptable for Wine.
2607 */
2608 if (strcmp(winetest_platform, "wine"))
2609 {
2611 {
2612 if (strcmp(ddeApplication, test->expectedDdeApplication[0]))
2613 which = 2;
2614 }
2615 else if (test == dde_default_app_tests + 1)
2616 {
2617 if (which == 0 && rc == test->rc[1])
2618 which = 1;
2619 trace("DDE result variant %d\n", which);
2620 }
2621 }
2622
2623 todo_wine_if(test->todo & 0x1)
2624 okShell(rc==test->rc[which], "failed: rc=%lu err=%u\n",
2625 rc, GetLastError());
2626 if (rc == 33)
2627 {
2628 todo_wine_if(test->todo & 0x2)
2629 ok(!strcmp(ddeApplication, test->expectedDdeApplication[which]),
2630 "Expected application '%s', got '%s'\n",
2631 test->expectedDdeApplication[which], ddeApplication);
2632 }
2634
2636 test++;
2637 }
2638
2639 ret = DdeNameService(ddeInst, hszApplication, 0, DNS_UNREGISTER);
2640 ok(ret != 0, "got %p\n", ret);
2642 ok(b, "got %d\n", b);
2643 b = DdeFreeStringHandle(ddeInst, hszApplication);
2644 ok(b, "got %d\n", b);
2646 ok(b, "got %d\n", b);
2647}
2648
2649static void init_test(void)
2650{
2651 HMODULE hdll;
2652 HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
2653 char filename[MAX_PATH];
2654 WCHAR lnkfile[MAX_PATH];
2655 char params[1024];
2656 const char* const * testfile;
2658 DWORD rc;
2659 HRESULT r;
2660
2661 hdll=GetModuleHandleA("shell32.dll");
2662 pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
2663 if (pDllGetVersion)
2664 {
2665 dllver.cbSize=sizeof(dllver);
2666 pDllGetVersion(&dllver);
2667 trace("major=%d minor=%d build=%d platform=%d\n",
2670 }
2671 else
2672 {
2673 memset(&dllver, 0, sizeof(dllver));
2674 }
2675
2676 r = CoInitialize(NULL);
2677 ok(r == S_OK, "CoInitialize failed (0x%08x)\n", r);
2678 if (FAILED(r))
2679 exit(1);
2680
2681 rc=GetModuleFileNameA(NULL, argv0, sizeof(argv0));
2682 ok(rc != 0 && rc < sizeof(argv0), "got %d\n", rc);
2684 {
2685 strcat(argv0, ".so");
2687 "unable to find argv0!\n");
2688 }
2689
2690 /* Older versions (win 2k) fail tests if there is a space in
2691 the path. */
2692 if (dllver.dwMajorVersion <= 5)
2693 strcpy(filename, "c:\\");
2694 else
2695 GetTempPathA(sizeof(filename), filename);
2696 GetTempFileNameA(filename, "wt", 0, tmpdir);
2699 rc = CreateDirectoryA( tmpdir, NULL );
2700 ok( rc, "failed to create %s err %u\n", tmpdir, GetLastError() );
2701 /* Set %TMPDIR% for the tests */
2703
2704 rc = GetTempFileNameA(tmpdir, "wt", 0, child_file);
2705 ok(rc != 0, "got %d\n", rc);
2707
2708 /* Set up the test files */
2709 testfile=testfiles;
2710 while (*testfile)
2711 {
2712 HANDLE hfile;
2713
2714 sprintf(filename, *testfile, tmpdir);
2717 if (hfile==INVALID_HANDLE_VALUE)
2718 {
2719 trace("unable to create '%s': err=%u\n", filename, GetLastError());
2720 assert(0);
2721 }
2722 CloseHandle(hfile);
2723 testfile++;
2724 }
2725
2726 /* Setup the test shortcuts */
2727 sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
2728 MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, ARRAY_SIZE(lnkfile));
2729 desc.description=NULL;
2730 desc.workdir=NULL;
2731 sprintf(filename, "%s\\test file.shlexec", tmpdir);
2732 desc.path=filename;
2733 desc.pidl=NULL;
2734 desc.arguments="ignored";
2735 desc.showcmd=0;
2736 desc.icon=NULL;
2737 desc.icon_id=0;
2738 desc.hotkey=0;
2739 create_lnk(lnkfile, &desc, 0);
2740
2741 sprintf(filename, "%s\\test_shortcut_exe.lnk", tmpdir);
2742 MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, ARRAY_SIZE(lnkfile));
2743 desc.description=NULL;
2744 desc.workdir=NULL;
2745 desc.path=argv0;
2746 desc.pidl=NULL;
2747 sprintf(params, "shlexec \"%s\" Lnk", child_file);
2748 desc.arguments=params;
2749 desc.showcmd=0;
2750 desc.icon=NULL;
2751 desc.icon_id=0;
2752 desc.hotkey=0;
2753 create_lnk(lnkfile, &desc, 0);
2754
2755 /* Create a basic association suitable for most tests */
2756 if (!create_test_association(".shlexec"))
2757 {
2759 skip("Unable to create association for '.shlexec'\n");
2760 return;
2761 }
2762 create_test_verb("shlexec.shlexec", "Open", 0, "Open \"%1\"");
2763 create_test_verb("shlexec.shlexec", "NoQuotes", 0, "NoQuotes %1");
2764 create_test_verb("shlexec.shlexec", "LowerL", 0, "LowerL %l");
2765 create_test_verb("shlexec.shlexec", "QuotedLowerL", 0, "QuotedLowerL \"%l\"");
2766 create_test_verb("shlexec.shlexec", "UpperL", 0, "UpperL %L");
2767 create_test_verb("shlexec.shlexec", "QuotedUpperL", 0, "QuotedUpperL \"%L\"");
2768
2770 create_test_verb("shlexec.sha", "averb", 0, "AVerb \"%1\"");
2771
2772 create_test_class("shlproto", TRUE);
2773 create_test_verb("shlproto", "open", 0, "URL \"%1\"");
2774 create_test_verb("shlproto", "averb", 0, "AVerb \"%1\"");
2775
2776 /* Set an environment variable to see if it is inherited */
2777 SetEnvironmentVariableA("ShlexecVar", "Present");
2778}
2779
2780static void cleanup_test(void)
2781{
2782 char filename[MAX_PATH];
2783 const char* const * testfile;
2784
2785 /* Delete the test files */
2786 testfile=testfiles;
2787 while (*testfile)
2788 {
2789 sprintf(filename, *testfile, tmpdir);
2790 /* Make sure we can delete the files ('test file.noassoc' is read-only now) */
2793 testfile++;
2794 }
2797
2798 /* Delete the test association */
2799 delete_test_association(".shlexec");
2801 delete_test_class("shlproto");
2802
2804
2806}
2807
2808static void test_directory(void)
2809{
2810 char path[MAX_PATH], curdir[MAX_PATH];
2811 char params[1024], dirpath[1024];
2812 INT_PTR rc;
2813
2814 sprintf(path, "%s\\test2.exe", tmpdir);
2816
2817 sprintf(params, "shlexec \"%s\" Exec", child_file);
2818
2819 /* Test with the current directory */
2820 GetCurrentDirectoryA(sizeof(curdir), curdir);
2823 NULL, "test2.exe", params, NULL, NULL);
2824 okShell(rc > 32, "returned %lu\n", rc);
2825 okChildInt("argcA", 4);
2826 okChildString("argvA3", "Exec");
2827 okChildPath("longPath", path);
2828 SetCurrentDirectoryA(curdir);
2829
2831 NULL, "test2.exe", params, NULL, NULL);
2832 okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
2833
2834 /* Explicitly specify the directory to use */
2836 NULL, "test2.exe", params, tmpdir, NULL);
2837 okShell(rc > 32, "returned %lu\n", rc);
2838 okChildInt("argcA", 4);
2839 okChildString("argvA3", "Exec");
2840 okChildPath("longPath", path);
2841
2842 /* Specify it through an environment variable */
2844 NULL, "test2.exe", params, "%TMPDIR%", NULL);
2845 todo_wine okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
2846
2848 NULL, "test2.exe", params, "%TMPDIR%", NULL);
2849 okShell(rc > 32, "returned %lu\n", rc);
2850 okChildInt("argcA", 4);
2851 okChildString("argvA3", "Exec");
2852 okChildPath("longPath", path);
2853
2854 /* Not a colon-separated directory list */
2855 sprintf(dirpath, "%s:%s", curdir, tmpdir);
2857 NULL, "test2.exe", params, dirpath, NULL);
2858 okShell(rc == SE_ERR_FNF, "returned %lu\n", rc);
2859}
2860
2862{
2863
2865 if (myARGC >= 3)
2866 {
2868 /* Skip the tests/failures trace for child processes */
2870 }
2871
2872 init_test();
2873
2875 test_argify();
2877 test_filename();
2878 test_fileurls();
2879 test_urls();
2881 test_lnks();
2882 test_exes();
2883 test_dde();
2886
2887 cleanup_test();
2888}
static int argc
Definition: ServiceArgs.c:12
#define broken(x)
Definition: _sntprintf.h:21
char * strcat(char *DstString, const char *SrcString)
Definition: utclib.c:568
int strcmp(const char *String1, const char *String2)
Definition: utclib.c:469
char * strstr(char *String1, char *String2)
Definition: utclib.c:653
ACPI_SIZE strlen(const char *String)
Definition: utclib.c:269
int strncmp(const char *String1, const char *String2, ACPI_SIZE Count)
Definition: utclib.c:534
int toupper(int c)
Definition: utclib.c:881
char * strcpy(char *DstString, const char *SrcString)
Definition: utclib.c:388
char * strchr(const char *String, int ch)
Definition: utclib.c:501
static void * heap_alloc(size_t len)
Definition: appwiz.h:66
static BOOL heap_free(void *mem)
Definition: appwiz.h:76
#define trace
Definition: atltest.h:70
#define ok(value,...)
Definition: atltest.h:57
#define skip(...)
Definition: atltest.h:64
#define START_TEST(x)
Definition: atltest.h:75
#define msg(x)
Definition: auth_time.c:54
void shell(int argc, const char *argv[])
Definition: cmds.c:1231
#define ARRAY_SIZE(A)
Definition: main.h:33
#define U(x)
Definition: wordpad.c:45
static HANDLE thread
Definition: service.c:33
#define RegCloseKey(hKey)
Definition: registry.h:49
Definition: _map.h:48
HSZ WINAPI DdeCreateStringHandleA(DWORD, LPCSTR, INT)
Definition: ddemisc.c:577
#define DNS_FILTEROFF
Definition: ddeml.h:155
#define CBF_FAIL_REQUESTS
Definition: ddeml.h:108
#define CBF_FAIL_POKES
Definition: ddeml.h:107
#define DNS_UNREGISTER
Definition: ddeml.h:153
INT WINAPI DdeCmpStringHandles(HSZ, HSZ)
Definition: ddemisc.c:685
#define DNS_REGISTER
Definition: ddeml.h:152
#define CBF_SKIP_ALLNOTIFICATIONS
Definition: ddeml.h:115
#define DMLERR_NO_ERROR
Definition: ddeml.h:242
BOOL WINAPI DdeUninitialize(DWORD)
Definition: ddemisc.c:1112
#define XTYP_CONNECT
Definition: ddeml.h:186
#define DDE_FACK
Definition: ddeml.h:216
DWORD WINAPI DdeQueryStringA(DWORD, HSZ, LPSTR, DWORD, INT)
Definition: ddemisc.c:501
BOOL WINAPI DdeFreeStringHandle(DWORD, HSZ)
Definition: ddemisc.c:631
#define CP_WINANSI
Definition: ddeml.h:32
#define XTYP_EXECUTE
Definition: ddeml.h:185
HDDEDATA WINAPI DdeNameService(DWORD, HSZ, HSZ, UINT)
Definition: ddeserver.c:154
#define CBF_FAIL_ADVISES
Definition: ddeml.h:105
BOOL WINAPI DdeFreeDataHandle(HDDEDATA)
Definition: ddemisc.c:1461
DWORD WINAPI DdeGetData(HDDEDATA, LPBYTE, DWORD, DWORD)
Definition: ddemisc.c:1379
#define SZDDESYS_TOPIC
Definition: ddeml.h:47
UINT WINAPI DdeInitializeA(LPDWORD, PFNCALLBACK, DWORD, DWORD)
Definition: ddemisc.c:1075
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define WAIT_TIMEOUT
Definition: dderror.h:14
#define ERROR_SUCCESS
Definition: deptool.c:10
static LSTATUS(WINAPI *pRegDeleteTreeW)(HKEY
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
LONG WINAPI RegSetValueExA(HKEY hKey, LPCSTR lpValueName, DWORD Reserved, DWORD dwType, CONST BYTE *lpData, DWORD cbData)
Definition: reg.c:4828
LONG WINAPI RegOpenKeyExA(_In_ HKEY hKey, _In_ LPCSTR lpSubKey, _In_ DWORD ulOptions, _In_ REGSAM samDesired, _Out_ PHKEY phkResult)
Definition: reg.c:3327
LONG WINAPI RegQueryInfoKeyA(HKEY hKey, LPSTR lpClass, LPDWORD lpcClass, LPDWORD lpReserved, LPDWORD lpcSubKeys, LPDWORD lpcMaxSubKeyLen, LPDWORD lpcMaxClassLen, LPDWORD lpcValues, LPDWORD lpcMaxValueNameLen, LPDWORD lpcMaxValueLen, LPDWORD lpcbSecurityDescriptor, PFILETIME lpftLastWriteTime)
Definition: reg.c:3612
LONG WINAPI RegEnumValueA(_In_ HKEY hKey, _In_ DWORD dwIndex, _Out_ LPSTR lpName, _Inout_ LPDWORD lpcbName, _Reserved_ LPDWORD lpdwReserved, _Out_opt_ LPDWORD lpdwType, _Out_opt_ LPBYTE lpData, _Inout_opt_ LPDWORD lpcbData)
Definition: reg.c:2697
LONG WINAPI RegDeleteValueA(HKEY hKey, LPCSTR lpValueName)
Definition: reg.c:2325
LONG WINAPI RegCreateKeyExA(_In_ HKEY hKey, _In_ LPCSTR lpSubKey, _In_ DWORD Reserved, _In_ LPSTR lpClass, _In_ DWORD dwOptions, _In_ REGSAM samDesired, _In_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, _Out_ PHKEY phkResult, _Out_ LPDWORD lpdwDisposition)
Definition: reg.c:1034
LONG WINAPI RegEnumKeyExA(_In_ HKEY hKey, _In_ DWORD dwIndex, _Out_ LPSTR lpName, _Inout_ LPDWORD lpcbName, _Reserved_ LPDWORD lpReserved, _Out_opt_ LPSTR lpClass, _Inout_opt_ LPDWORD lpcbClass, _Out_opt_ PFILETIME lpftLastWriteTime)
Definition: reg.c:2448
LONG WINAPI RegDeleteKeyA(_In_ HKEY hKey, _In_ LPCSTR lpSubKey)
Definition: reg.c:1224
#define CloseHandle
Definition: compat.h:739
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define UnmapViewOfFile
Definition: compat.h:746
#define CP_ACP
Definition: compat.h:109
#define lstrcpynA
Definition: compat.h:751
#define SetLastError(x)
Definition: compat.h:752
#define GetProcAddress(x, y)
Definition: compat.h:753
#define INVALID_HANDLE_VALUE
Definition: compat.h:731
#define CreateFileA(a, b, c, d, e, f, g)
Definition: compat.h:740
#define MAX_PATH
Definition: compat.h:34
#define FILE_MAP_READ
Definition: compat.h:776
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:137
#define CALLBACK
Definition: compat.h:35
#define GetEnvironmentVariableA(x, y, z)
Definition: compat.h:754
#define MapViewOfFile
Definition: compat.h:745
#define MultiByteToWideChar
Definition: compat.h:110
#define ERROR_ACCESS_DENIED
Definition: compat.h:97
static void cleanup(void)
Definition: main.c:1335
BOOL WINAPI DECLSPEC_HOTPATCH SetEnvironmentVariableA(IN LPCSTR lpName, IN LPCSTR lpValue)
Definition: environ.c:218
BOOL WINAPI CopyFileA(IN LPCSTR lpExistingFileName, IN LPCSTR lpNewFileName, IN BOOL bFailIfExists)
Definition: copy.c:404
BOOL WINAPI DeleteFileA(IN LPCSTR lpFileName)
Definition: delete.c:24
BOOL WINAPI RemoveDirectoryA(IN LPCSTR lpPathName)
Definition: dir.c:714
BOOL WINAPI CreateDirectoryA(IN LPCSTR lpPathName, IN LPSECURITY_ATTRIBUTES lpSecurityAttributes)
Definition: dir.c:37
BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
Definition: fileinfo.c:776
DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName)
Definition: fileinfo.c:636
HANDLE WINAPI FindFirstFileA(IN LPCSTR lpFileName, OUT LPWIN32_FIND_DATAA lpFindFileData)
Definition: find.c:263
BOOL WINAPI FindClose(HANDLE hFindFile)
Definition: find.c:502
BOOL WINAPI WriteFile(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, OUT LPDWORD lpNumberOfBytesWritten, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: rw.c:24
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:600
HMODULE WINAPI DECLSPEC_HOTPATCH GetModuleHandleA(LPCSTR lpModuleName)
Definition: loader.c:812
DWORD WINAPI GetModuleFileNameA(HINSTANCE hModule, LPSTR lpFilename, DWORD nSize)
Definition: loader.c:539
DWORD WINAPI GetCurrentDirectoryA(IN DWORD nBufferLength, OUT LPSTR lpBuffer)
Definition: path.c:2146
DWORD WINAPI GetShortPathNameA(IN LPCSTR lpszLongPath, OUT LPSTR lpszShortPath, IN DWORD cchBuffer)
Definition: path.c:1752
DWORD WINAPI GetLongPathNameA(IN LPCSTR lpszShortPath, OUT LPSTR lpszLongPath, IN DWORD cchBuffer)
Definition: path.c:1671
BOOL WINAPI SetCurrentDirectoryA(IN LPCSTR lpPathName)
Definition: path.c:2206
DWORD WINAPI GetTempPathA(IN DWORD nBufferLength, OUT LPSTR lpBuffer)
Definition: path.c:2054
UINT WINAPI GetSystemDirectoryA(OUT LPSTR lpBuffer, IN UINT uSize)
Definition: path.c:2283
BOOL WINAPI GetExitCodeProcess(IN HANDLE hProcess, IN LPDWORD lpExitCode)
Definition: proc.c:1168
VOID WINAPI ExitProcess(IN UINT uExitCode)
Definition: proc.c:1487
LPSTR WINAPI GetCommandLineA(VOID)
Definition: proc.c:2003
VOID WINAPI ExitThread(IN DWORD uExitCode)
Definition: thread.c:365
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:137
INT WINAPI GetPrivateProfileStringA(LPCSTR section, LPCSTR entry, LPCSTR def_val, LPSTR buffer, UINT len, LPCSTR filename)
Definition: profile.c:1204
UINT WINAPI GetPrivateProfileIntA(LPCSTR section, LPCSTR entry, INT def_val, LPCSTR filename)
Definition: profile.c:1326
BOOL WINAPI DECLSPEC_HOTPATCH WritePrivateProfileStringA(LPCSTR section, LPCSTR entry, LPCSTR string, LPCSTR filename)
Definition: profile.c:1484
HRESULT WINAPI CoInitialize(LPVOID lpReserved)
Definition: compobj.c:1964
void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
Definition: compobj.c:2067
#define assert(x)
Definition: debug.h:53
#define ULONG_PTR
Definition: config.h:101
#define strcasecmp
Definition: fake.h:9
HANDLE NTAPI CreateFileMappingA(IN HANDLE hFile, IN LPSECURITY_ATTRIBUTES lpFileMappingAttributes, IN DWORD flProtect, IN DWORD dwMaximumSizeHigh, IN DWORD dwMaximumSizeLow, IN LPCSTR lpName)
Definition: filemap.c:23
HANDLE NTAPI OpenFileMappingA(IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN LPCSTR lpName)
Definition: filemap.c:284
UINT WINAPI GetTempFileNameA(IN LPCSTR lpPathName, IN LPCSTR lpPrefixString, IN UINT uUnique, OUT LPSTR lpTempFileName)
Definition: filename.c:26
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS
Definition: ntddk_ex.h:187
FxAutoRegKey hKey
GLdouble s
Definition: gl.h:2039
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
GLsizeiptr size
Definition: glext.h:5919
GLuint buffer
Definition: glext.h:5915
GLenum condition
Definition: glext.h:9255
const GLubyte * c
Definition: glext.h:8905
GLenum GLint GLuint mask
Definition: glext.h:6028
GLboolean GLboolean GLboolean b
Definition: glext.h:6204
GLenum const GLfloat * params
Definition: glext.h:5645
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
GLfloat GLfloat p
Definition: glext.h:8902
GLfloat param
Definition: glext.h:5796
GLenum GLsizei len
Definition: glext.h:6722
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:6102
GLuint64EXT * result
Definition: glext.h:11304
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
HLOCAL NTAPI LocalFree(HLOCAL hMem)
Definition: heapmem.c:1594
#define stderr
Definition: stdio.h:100
_Check_return_opt_ _CRTIMP int __cdecl fprintf(_Inout_ FILE *_File, _In_z_ _Printf_format_string_ const char *_Format,...)
int __cdecl vsprintf(char *_Dest, const char *_Format, va_list _Args)
Definition: sprintf.c:733
#define S_OK
Definition: intsafe.h:52
#define FAILED(hr)
Definition: intsafe.h:51
const char * filename
Definition: ioapi.h:137
#define c
Definition: ke_i.h:80
#define b
Definition: ke_i.h:79
#define wine_dbgstr_w
Definition: kernel32.h:34
#define trace_(file, line,...)
Definition: kmt_test.h:223
#define REG_SZ
Definition: layer.c:22
int WINAPI lstrcmpiA(LPCSTR lpString1, LPCSTR lpString2)
Definition: lstring.c:42
int WINAPI lstrcmpW(LPCWSTR lpString1, LPCWSTR lpString2)
Definition: lstring.c:170
int WINAPI lstrcmpA(LPCSTR lpString1, LPCSTR lpString2)
Definition: lstring.c:18
struct S1 s1
struct S2 s2
__u16 time
Definition: mkdosfs.c:8
#define CREATE_ALWAYS
Definition: disk.h:72
#define ERROR_FILE_NOT_FOUND
Definition: disk.h:79
PSDBQUERYRESULT_VISTA PVOID DWORD * dwSize
Definition: env.c:56
static PVOID ptr
Definition: dispmode.c:27
#define sprintf(buf, format,...)
Definition: sprintf.c:55
static char shortpath[MAX_PATH]
Definition: batch.c:32
static const WCHAR url[]
Definition: encode.c:1432
static const WCHAR desc[]
Definition: protectdata.c:36
BOOL expected
Definition: store.c:2063
#define todo_wine_if(is_todo)
Definition: custom.c:76
#define todo_wine
Definition: custom.c:79
static const WCHAR sp[]
Definition: suminfo.c:287
static __ms_va_list valist
Definition: printf.c:66
WCHAR strW[12]
Definition: clipboard.c:2029
DWORD e_lfanew
Definition: crypt.c:1156
#define argv
Definition: mplay32.c:18
unsigned __int3264 UINT_PTR
Definition: mstsclib_h.h:274
_In_ HANDLE hFile
Definition: mswsock.h:90
unsigned int UINT
Definition: ndis.h:50
#define PAGE_READWRITE
Definition: nt_native.h:1304
#define KEY_READ
Definition: nt_native.h:1023
#define KEY_CREATE_SUB_KEY
Definition: nt_native.h:1018
#define GENERIC_WRITE
Definition: nt_native.h:90
#define KEY_SET_VALUE
Definition: nt_native.h:1017
struct _IMAGE_IMPORT_DESCRIPTOR * PIMAGE_IMPORT_DESCRIPTOR
#define IMAGE_SNAP_BY_ORDINAL(Ordinal)
Definition: ntimage.h:567
PIMAGE_THUNK_DATA32 PIMAGE_THUNK_DATA
Definition: ntimage.h:566
#define IMAGE_DIRECTORY_ENTRY_IMPORT
Definition: pedump.c:260
long LONG
Definition: pedump.c:60
struct _IMAGE_IMPORT_BY_NAME * PIMAGE_IMPORT_BY_NAME
static const char topic[]
Definition: propsys.c:44
#define equal(x, y)
Definition: reader.cc:56
WCHAR classname[128]
Definition: startup.c:15
#define test
Definition: rosglue.h:37
const WCHAR * str
#define WINAPIV
Definition: sdbpapi.h:64
_Check_return_ _CRTIMP _CONST_RETURN char *__cdecl strrchr(_In_z_ const char *_Str, _In_ int _Ch)
const char * winetest_platform
int winetest_debug
#define win_skip
Definition: test.h:160
LONG winetest_get_failures(void)
void __winetest_cdecl winetest_ok(int condition, const char *msg,...)
int winetest_get_mainargs(char ***pargv)
#define WINETEST_PRINTF_ATTR(fmt, args)
Definition: test.h:140
void winetest_add_failures(LONG new_failures)
#define exit(n)
Definition: config.h:202
#define memset(x, y, z)
Definition: compat.h:39
#define args
Definition: format.c:66
LPWSTR *WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int *numargs)
Definition: shell32_main.c:80
#define create_lnk(a, b, c)
Definition: shell32_test.h:37
#define SEE_MASK_DOENVSUBST
Definition: shellapi.h:35
#define SEE_MASK_NOCLOSEPROCESS
Definition: shellapi.h:31
#define SE_ERR_PNF
Definition: shellapi.h:123
#define SEE_MASK_CLASSNAME
Definition: shellapi.h:25
#define SE_ERR_ACCESSDENIED
Definition: shellapi.h:124
#define SEE_MASK_NOZONECHECKS
Definition: shellapi.h:54
#define SEE_MASK_WAITFORINPUTIDLE
Definition: shellapi.h:56
#define SEE_MASK_FLAG_DDEWAIT
Definition: shellapi.h:34
#define SE_ERR_FNF
Definition: shellapi.h:122
#define SEE_MASK_FLAG_NO_UI
Definition: shellapi.h:36
#define SE_ERR_NOASSOC
Definition: shellapi.h:132
#define SEE_MASK_NO_CONSOLE
Definition: shellapi.h:38
#define SE_ERR_DDEFAIL
Definition: shellapi.h:130
static PVOID hdll
Definition: shimdbg.c:126
static INT_PTR shell_execute_ex_(const char *file, int line, DWORD mask, LPCSTR verb, LPCSTR filename, LPCSTR parameters, LPCSTR directory, LPCSTR class)
Definition: shlexec.c:572
static filename_tests_t noquotes_tests[]
Definition: shlexec.c:1082
#define okShell
Definition: shlexec.c:362
static void test_urls(void)
Definition: shlexec.c:1869
static HDDEDATA CALLBACK ddeCb(UINT uType, UINT uFmt, HCONV hConv, HSZ hsz1, HSZ hsz2, HDDEDATA hData, ULONG_PTR dwData1, ULONG_PTR dwData2)
Definition: shlexec.c:153
static void dump_child_(const char *file, int line)
Definition: shlexec.c:312
static void create_test_verb(const char *classname, const char *verb, int rawcmd, const char *cmdtail)
Definition: shlexec.c:915
#define shell_execute(verb, filename, parameters, directory)
Definition: shlexec.c:569
static DLLVERSIONINFO dllver
Definition: shlexec.c:54
static void test_dde_default_app(void)
Definition: shlexec.c:2549
static void test_find_executable(void)
Definition: shlexec.c:1963
static void test_dde(void)
Definition: shlexec.c:2382
#define shell_execute_ex(mask, verb, filename, parameters, directory, class)
Definition: shlexec.c:674
#define CHILD_DDE_TIMEOUT
Definition: shlexec.c:146
static const char * encodeA(const char *str)
Definition: shlexec.c:67
static void okChildInt_(const char *file, int line, const char *key, int expected)
Definition: shlexec.c:453
static BOOL post_quit_on_execute
Definition: shlexec.c:150
static char child_file[MAX_PATH]
Definition: shlexec.c:53
static int bad_shellexecute
Definition: shlexec.c:494
static HANDLE dde_ready_event
Definition: shlexec.c:57
#define okChildString(key, expected)
Definition: shlexec.c:383
static void test_directory(void)
Definition: shlexec.c:2808
static void create_test_verb_dde(const char *classname, const char *verb, int rawcmd, const char *cmdtail, const char *ddeexec, const char *application, const char *topic, const char *ifexec)
Definition: shlexec.c:814
static void test_commandline2argv(void)
Definition: shlexec.c:1359
static void test_exes(void)
Definition: shlexec.c:2204
#define todo_wait
Definition: shlexec.c:492
static void reset_association_description(void)
Definition: shlexec.c:365
static int _todo_wait
Definition: shlexec.c:491
static BOOL test_one_cmdline(const cmdline_tests_t *test)
Definition: shlexec.c:1318
static const argify_tests_t argify_tests[]
Definition: shlexec.c:1420
static fileurl_tests_t fileurl_tests[]
Definition: shlexec.c:1750
static char shell_call[2048]
Definition: shlexec.c:348
static const char * testfiles[]
Definition: shlexec.c:1005
static DWORD ddeInst
Definition: shlexec.c:147
static void test_lnks(void)
Definition: shlexec.c:2110
static int myARGC
Definition: shlexec.c:50
static unsigned decode_char(char c)
Definition: shlexec.c:87
static dde_default_app_tests_t dde_default_app_tests[]
Definition: shlexec.c:2488
static DWORD get_long_path_name(const char *shortpath, char *longpath, DWORD longlen)
Definition: shlexec.c:930
static HANDLE hEvent
Definition: shlexec.c:189
static HSZ hszTopic
Definition: shlexec.c:148
static BOOL skip_shlexec_tests
Definition: shlexec.c:55
#define URL_SUCCESS
Definition: shlexec.c:1746
#define okChildStringBroken(key, expected, broken)
Definition: shlexec.c:384
static char * decodeA(const char *str)
Definition: shlexec.c:95
static int StrCmpPath(const char *s1, const char *s2)
Definition: shlexec.c:386
static INT_PTR shell_execute_(const char *file, int line, LPCSTR verb, LPCSTR filename, LPCSTR parameters, LPCSTR directory)
Definition: shlexec.c:496
#define okShell_(file, line)
Definition: shlexec.c:361
static void test_filename(void)
Definition: shlexec.c:1603
static void test_fileurls(void)
Definition: shlexec.c:1786
static void hook_WaitForInputIdle(DWORD(WINAPI *new_func)(HANDLE, DWORD))
Definition: shlexec.c:2321
static void CALLBACK childTimeout(HWND wnd, UINT msg, UINT_PTR timer, DWORD time)
Definition: shlexec.c:201
static DWORD WINAPI hooked_WaitForInputIdle(HANDLE process, DWORD timeout)
Definition: shlexec.c:2303
static void init_test(void)
Definition: shlexec.c:2649
#define okChildInt(key, expected)
Definition: shlexec.c:460
static const cmdline_tests_t cmdline_tests[]
Definition: shlexec.c:1157
static int waitforinputidle_count
Definition: shlexec.c:2302
static char ** myARGV
Definition: shlexec.c:51
static filename_tests_t lnk_tests[]
Definition: shlexec.c:2094
static BOOL create_test_class(const char *class, BOOL protocol)
Definition: shlexec.c:684
static void okChildString_(const char *file, int line, const char *key, const char *expected, const char *bad)
Definition: shlexec.c:370
static void okChildIntBroken_(const char *file, int line, const char *key, int expected)
Definition: shlexec.c:462
static char ddeExec[MAX_PATH]
Definition: shlexec.c:149
static void test_argify(void)
Definition: shlexec.c:1545
#define okChildIntBroken(key, expected)
Definition: shlexec.c:469
#define DDE_DEFAULT_APP_VARIANTS
Definition: shlexec.c:2479
static filename_tests_t filename_tests[]
Definition: shlexec.c:1039
static void init_event(const char *child_file)
Definition: shlexec.c:190
static char tmpdir[MAX_PATH]
Definition: shlexec.c:52
#define USE_COLON
Definition: shlexec.c:1747
static void delete_test_class(const char *classname)
Definition: shlexec.c:800
static char ddeApplication[MAX_PATH]
Definition: shlexec.c:149
#define USE_BSLASH
Definition: shlexec.c:1748
static BOOL create_test_association(const char *extension)
Definition: shlexec.c:712
static char assoc_desc[2048]
Definition: shlexec.c:364
static void delete_test_association(const char *extension)
Definition: shlexec.c:805
#define okChildPath(key, expected)
Definition: shlexec.c:451
static void doChild(int argc, char **argv)
Definition: shlexec.c:208
static char argv0[MAX_PATH]
Definition: shlexec.c:49
static LSTATUS myRegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
Definition: shlexec.c:734
static dde_tests_t dde_tests[]
Definition: shlexec.c:2274
static void strcat_param(char *str, const char *name, const char *param)
Definition: shlexec.c:478
static DWORD CALLBACK ddeThread(LPVOID arg)
Definition: shlexec.c:2538
static void cleanup_test(void)
Definition: shlexec.c:2780
static BOOL skip_noassoc_tests
Definition: shlexec.c:56
static char * getChildString(const char *sect, const char *key)
Definition: shlexec.c:127
static void okChildPath_(const char *file, int line, const char *key, const char *expected)
Definition: shlexec.c:423
static void test_lpFile_parsed(void)
Definition: shlexec.c:1092
BOOL WINAPI DECLSPEC_HOTPATCH ShellExecuteExA(LPSHELLEXECUTEINFOA sei)
Definition: shlexec.cpp:2318
HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
Definition: shlexec.cpp:1214
HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd)
Definition: shlexec.cpp:2284
DWORD dwMajorVersion
Definition: shlwapi.h:1955
DWORD dwBuildNumber
Definition: shlwapi.h:1957
DWORD dwMinorVersion
Definition: shlwapi.h:1956
DWORD dwPlatformID
Definition: shlwapi.h:1958
IMAGE_OPTIONAL_HEADER32 OptionalHeader
Definition: ntddk_ex.h:184
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]
Definition: ntddk_ex.h:178
union _IMAGE_THUNK_DATA32::@2129 u1
LPCSTR lpParameters
Definition: shellapi.h:313
HINSTANCE hInstApp
Definition: shellapi.h:316
LPCSTR lpDirectory
Definition: shellapi.h:314
const char * cmd
Definition: shlexec.c:1416
const char * verb
Definition: shlexec.c:1413
const char * params
Definition: shlexec.c:1414
const char * broken
Definition: shlexec.c:1417
Definition: match.c:390
Definition: ftp_var.h:139
const char * cmd
Definition: shlexec.c:1152
const char * command
Definition: shlexec.c:2482
BOOL broken
Definition: shlexec.c:2271
const char * expectedDdeExec
Definition: shlexec.c:2270
const char * topic
Definition: shlexec.c:2267
const char * application
Definition: shlexec.c:2266
const char * ddeexec
Definition: shlexec.c:2265
const char * ifexec
Definition: shlexec.c:2268
const char * command
Definition: shlexec.c:2264
int expectedArgs
Definition: shlexec.c:2269
DWORD threadIdParent
Definition: shlexec.c:2535
Definition: fci.c:127
const char * verb
Definition: shlexec.c:1033
const char * basename
Definition: shlexec.c:1034
const char * basename
Definition: shlexec.c:1741
const char * urlprefix
Definition: shlexec.c:1740
Definition: dsound.c:943
Definition: copy.c:22
Definition: parser.c:49
Definition: name.c:39
Definition: dhcpd.h:245
#define max(a, b)
Definition: svc.c:63
DWORD WINAPI WaitForSingleObject(IN HANDLE hHandle, IN DWORD dwMilliseconds)
Definition: synch.c:82
VOID WINAPI DECLSPEC_HOTPATCH Sleep(IN DWORD dwMilliseconds)
Definition: synch.c:790
HANDLE WINAPI DECLSPEC_HOTPATCH CreateEventA(IN LPSECURITY_ATTRIBUTES lpEventAttributes OPTIONAL, IN BOOL bManualReset, IN BOOL bInitialState, IN LPCSTR lpName OPTIONAL)
Definition: synch.c:637
BOOL WINAPI DECLSPEC_HOTPATCH SetEvent(IN HANDLE hEvent)
Definition: synch.c:733
HANDLE WINAPI DECLSPEC_HOTPATCH OpenEventA(IN DWORD dwDesiredAccess, IN BOOL bInheritHandle, IN LPCSTR lpName)
Definition: synch.c:669
TW_UINT32 TW_UINT16 TW_UINT16 MSG
Definition: twain.h:1829
int32_t INT_PTR
Definition: typedefs.h:64
unsigned char * LPBYTE
Definition: typedefs.h:53
PVOID HANDLE
Definition: typedefs.h:73
int32_t INT
Definition: typedefs.h:58
uint32_t ULONG_PTR
Definition: typedefs.h:65
#define INVALID_FILE_ATTRIBUTES
Definition: vfdcmd.c:23
int ret
static GLenum which
Definition: wgl_font.c:159
BOOL NTAPI VirtualProtect(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flNewProtect, OUT PDWORD lpflOldProtect)
Definition: virtmem.c:135
#define success(from, fromstr, to, tostr)
DWORD WINAPI GetLastError(void)
Definition: except.c:1042
#define FILE_MAP_WRITE
Definition: winbase.h:154
DWORD WINAPI GetCurrentThreadId(void)
Definition: thread.c:459
#define EVENT_MODIFY_STATE
Definition: winbase.h:163
#define WAIT_OBJECT_0
Definition: winbase.h:406
#define __ms_va_list
Definition: windef.h:456
#define __ms_va_end(list)
Definition: windef.h:458
#define __ms_va_start(list, arg)
Definition: windef.h:457
void * arg
Definition: msvc.h:10
#define HRESULT
Definition: msvc.h:7
#define WINAPI
Definition: msvc.h:6
#define HKEY_CLASSES_ROOT
Definition: winreg.h:10
#define SW_SHOWNORMAL
Definition: winuser.h:770
LRESULT WINAPI DispatchMessageA(_In_ const MSG *)
#define SW_HIDE
Definition: winuser.h:768
#define WM_CLOSE
Definition: winuser.h:1621
#define WM_QUIT
Definition: winuser.h:1623
__analysis_noreturn void WINAPI PostQuitMessage(_In_ int)
LRESULT WINAPI SendMessageA(_In_ HWND, _In_ UINT, _In_ WPARAM, _In_ LPARAM)
UINT_PTR WINAPI SetTimer(_In_opt_ HWND, _In_ UINT_PTR, _In_ UINT, _In_opt_ TIMERPROC)
DWORD WINAPI WaitForInputIdle(_In_ HANDLE, _In_ DWORD)
BOOL WINAPI PostThreadMessageA(_In_ DWORD, _In_ UINT, _In_ WPARAM, _In_ LPARAM)
BOOL WINAPI KillTimer(_In_opt_ HWND, _In_ UINT_PTR)
BOOL WINAPI GetMessageA(_Out_ LPMSG, _In_opt_ HWND, _In_ UINT, _In_ UINT)
HWND WINAPI FindWindowA(_In_opt_ LPCSTR, _In_opt_ LPCSTR)
const char * LPCSTR
Definition: xmlstorage.h:183
__wchar_t WCHAR
Definition: xmlstorage.h:180
WCHAR * LPWSTR
Definition: xmlstorage.h:184
char CHAR
Definition: xmlstorage.h:175
#define const
Definition: zconf.h:233