1 /*
2  * Unit tests for IShellDispatch
3  *
4  * Copyright 2010 Alexander Morozov for Etersoft
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 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24 
25 #include "shldisp.h"
26 #include "shlobj.h"
27 #include "shlwapi.h"
28 #include "winsvc.h"
29 
30 #include "wine/heap.h"
31 #include "wine/test.h"
32 
33 #include "initguid.h"
34 
35 #define EXPECT_HR(hr,hr_exp) \
36     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
37 
38 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown *)obj, ref, __LINE__)
39 static void _expect_ref(IUnknown *obj, ULONG ref, int line)
40 {
41     ULONG rc;
42     IUnknown_AddRef(obj);
43     rc = IUnknown_Release(obj);
44     ok_(__FILE__,line)(rc == ref, "Unexpected refcount %d, expected %d\n", rc, ref);
45 }
46 
47 static const WCHAR winetestW[] = {'w','i','n','e','t','e','s','t',0};
48 
49 static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*);
50 
51 /* Updated Windows 7 has a new IShellDispatch6 in its typelib */
52 DEFINE_GUID(IID_IWin7ShellDispatch6, 0x34936ba1, 0x67ad, 0x4c41, 0x99,0xb8, 0x8c,0x12,0xdf,0xf1,0xe9,0x74);
53 
54 static BSTR a2bstr(const char *str)
55 {
56     BSTR ret;
57     int len;
58 
59     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
60     ret = SysAllocStringLen(NULL, len);
61     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
62 
63     return ret;
64 }
65 
66 static void variant_set_string(VARIANT *v, const char *s)
67 {
68     V_VT(v) = VT_BSTR;
69     V_BSTR(v) = a2bstr(s);
70 }
71 
72 static void init_function_pointers(void)
73 {
74     HMODULE hshell32;
75 
76     hshell32 = GetModuleHandleA("shell32.dll");
77     pSHGetNameFromIDList = (void*)GetProcAddress(hshell32, "SHGetNameFromIDList");
78 }
79 
80 static void test_namespace(void)
81 {
82     static const ShellSpecialFolderConstants special_folders[] =
83     {
84         ssfDESKTOP,
85         ssfPROGRAMS,
86         ssfCONTROLS,
87         ssfPRINTERS,
88         ssfPERSONAL,
89         ssfFAVORITES,
90         ssfSTARTUP,
91         ssfRECENT,
92         ssfSENDTO,
93         ssfBITBUCKET,
94         ssfSTARTMENU,
95         ssfDESKTOPDIRECTORY,
96         ssfDRIVES,
97         ssfNETWORK,
98         ssfNETHOOD,
99         ssfFONTS,
100         ssfTEMPLATES,
101         ssfCOMMONSTARTMENU,
102         ssfCOMMONPROGRAMS,
103         ssfCOMMONSTARTUP,
104         ssfCOMMONDESKTOPDIR,
105         ssfAPPDATA,
106         ssfPRINTHOOD,
107         ssfLOCALAPPDATA,
108         ssfALTSTARTUP,
109         ssfCOMMONALTSTARTUP,
110         ssfCOMMONFAVORITES,
111         ssfINTERNETCACHE,
112         ssfCOOKIES,
113         ssfHISTORY,
114         ssfCOMMONAPPDATA,
115         ssfWINDOWS,
116         ssfSYSTEM,
117         ssfPROGRAMFILES,
118         ssfMYPICTURES,
119         ssfPROFILE,
120         ssfSYSTEMx86,
121         ssfPROGRAMFILESx86,
122     };
123 
124     static const WCHAR backslashW[] = {'\\',0};
125     static const WCHAR clsidW[] = {
126         ':',':','{','6','4','5','F','F','0','4','0','-','5','0','8','1','-',
127                     '1','0','1','B','-','9','F','0','8','-',
128                     '0','0','A','A','0','0','2','F','9','5','4','E','}',0};
129 
130     static WCHAR tempW[MAX_PATH], curW[MAX_PATH];
131     WCHAR *long_pathW = NULL;
132     HRESULT r;
133     IShellDispatch *sd;
134     Folder *folder;
135     Folder2 *folder2;
136     FolderItem *item;
137     VARIANT var;
138     BSTR title, item_path;
139     IDispatch *disp;
140     int len, i;
141 
142     r = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, &IID_IShellDispatch, (void **)&sd);
143     ok(SUCCEEDED(r), "Failed to create ShellDispatch object: %#x.\n", r);
144 
145     disp = NULL;
146     r = IShellDispatch_get_Application(sd, &disp);
147     ok(r == S_OK, "Failed to get application pointer, hr %#x.\n", r);
148     ok(disp == (IDispatch *)sd, "Unexpected application pointer %p.\n", disp);
149     IDispatch_Release(disp);
150 
151     disp = NULL;
152     r = IShellDispatch_get_Parent(sd, &disp);
153     ok(r == S_OK, "Failed to get Shell object parent, hr %#x.\n", r);
154     ok(disp == (IDispatch *)sd, "Unexpected parent pointer %p.\n", disp);
155     IDispatch_Release(disp);
156 
157     VariantInit(&var);
158     folder = (void*)0xdeadbeef;
159     r = IShellDispatch_NameSpace(sd, var, &folder);
160     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
161     ok(folder == NULL, "expected NULL, got %p\n", folder);
162 
163     /* test valid folder ids */
164     for (i = 0; i < ARRAY_SIZE(special_folders); i++)
165     {
166         V_VT(&var) = VT_I4;
167         V_I4(&var) = special_folders[i];
168         folder = (void*)0xdeadbeef;
169         r = IShellDispatch_NameSpace(sd, var, &folder);
170         if (special_folders[i] == ssfALTSTARTUP || special_folders[i] == ssfCOMMONALTSTARTUP)
171         todo_wine
172             ok(r == S_OK || broken(r == S_FALSE) /* winxp */, "Failed to get folder for index %#x, got %08x\n", special_folders[i], r);
173         else
174             ok(r == S_OK, "Failed to get folder for index %#x, got %08x\n", special_folders[i], r);
175         if (folder)
176             Folder_Release(folder);
177     }
178 
179     V_VT(&var) = VT_I4;
180     V_I4(&var) = -1;
181     folder = (void *)0xdeadbeef;
182     r = IShellDispatch_NameSpace(sd, var, &folder);
183     ok(r == S_FALSE, "Unexpected hr %#x.\n", r);
184     ok(folder == NULL, "Unexpected folder instance %p\n", folder);
185 
186     V_VT(&var) = VT_I4;
187     V_I4(&var) = ssfPROGRAMFILES;
188     r = IShellDispatch_NameSpace(sd, var, &folder);
189     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
190     if (r == S_OK)
191     {
192         static WCHAR path[MAX_PATH];
193 
194         r = SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, path);
195         ok(r == S_OK, "Failed to get folder path: %#x.\n", r);
196 
197         r = Folder_get_Title(folder, &title);
198         ok(r == S_OK, "Folder::get_Title failed: %08x\n", r);
199         if (r == S_OK)
200         {
201             /* On Win2000-2003 title is equal to program files directory name in
202                HKLM\Software\Microsoft\Windows\CurrentVersion\ProgramFilesDir.
203                On newer Windows it seems constant and is not changed
204                if the program files directory name is changed */
205             if (pSHGetNameFromIDList)
206             {
207                 LPITEMIDLIST pidl;
208                 PWSTR name;
209 
210                 r = SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl);
211                 ok(r == S_OK, "SHGetSpecialFolderLocation failed: %08x\n", r);
212                 r = pSHGetNameFromIDList(pidl, SIGDN_NORMALDISPLAY, &name);
213                 ok(r == S_OK, "SHGetNameFromIDList failed: %08x\n", r);
214                 ok(!lstrcmpW(title, name), "expected %s, got %s\n", wine_dbgstr_w(name), wine_dbgstr_w(title));
215                 CoTaskMemFree(name);
216                 CoTaskMemFree(pidl);
217             }
218             else
219             {
220                 WCHAR *p;
221 
222                 p = path + lstrlenW(path);
223                 while (path < p && *(p - 1) != '\\')
224                     p--;
225                 ok(!lstrcmpiW(title, p), "expected %s, got %s\n",
226                  wine_dbgstr_w(p), wine_dbgstr_w(title));
227             }
228             SysFreeString(title);
229         }
230         r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
231         ok(r == S_OK, "Folder::QueryInterface failed: %08x\n", r);
232         if (r == S_OK)
233         {
234             r = Folder2_get_Self(folder2, &item);
235             ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
236             if (r == S_OK)
237             {
238                 r = FolderItem_get_Path(item, &item_path);
239                 ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
240                 ok(!lstrcmpiW(item_path, path), "expected %s, got %s\n", wine_dbgstr_w(path), wine_dbgstr_w(item_path));
241                 SysFreeString(item_path);
242                 FolderItem_Release(item);
243             }
244             Folder2_Release(folder2);
245         }
246         Folder_Release(folder);
247     }
248 
249     V_VT(&var) = VT_I4;
250     V_I4(&var) = ssfBITBUCKET;
251     r = IShellDispatch_NameSpace(sd, var, &folder);
252     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
253 
254     r = Folder_QueryInterface(folder, &IID_Folder2, (void **)&folder2);
255     ok(r == S_OK, "Failed to get Folder2 interface: %#x.\n", r);
256     r = Folder2_get_Self(folder2, &item);
257     ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
258     r = FolderItem_get_Path(item, &item_path);
259     ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
260     /* TODO: we return lowercase GUID here */
261     ok(!lstrcmpiW(item_path, clsidW), "expected %s, got %s\n", wine_dbgstr_w(clsidW), wine_dbgstr_w(item_path));
262 
263     SysFreeString(item_path);
264     FolderItem_Release(item);
265     Folder2_Release(folder2);
266     Folder_Release(folder);
267 
268     GetTempPathW(MAX_PATH, tempW);
269     GetCurrentDirectoryW(MAX_PATH, curW);
270     SetCurrentDirectoryW(tempW);
271     CreateDirectoryW(winetestW, NULL);
272     V_VT(&var) = VT_BSTR;
273     V_BSTR(&var) = SysAllocString(winetestW);
274     r = IShellDispatch_NameSpace(sd, var, &folder);
275     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
276     SysFreeString(V_BSTR(&var));
277 
278     GetFullPathNameW(winetestW, MAX_PATH, tempW, NULL);
279 
280     len = GetLongPathNameW(tempW, NULL, 0);
281     long_pathW = heap_alloc(len * sizeof(WCHAR));
282     GetLongPathNameW(tempW, long_pathW, len);
283 
284     V_VT(&var) = VT_BSTR;
285     V_BSTR(&var) = SysAllocString(tempW);
286     r = IShellDispatch_NameSpace(sd, var, &folder);
287     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
288 
289     disp = (void *)0xdeadbeef;
290     r = Folder_get_Parent(folder, &disp);
291     ok(r == E_NOTIMPL, "Unexpected hr %#x.\n", r);
292     ok(disp == NULL, "Unexpected parent pointer %p.\n", disp);
293 
294     r = Folder_get_Title(folder, &title);
295     ok(r == S_OK, "Failed to get folder title: %#x.\n", r);
296     ok(!lstrcmpW(title, winetestW), "Unexpected title: %s\n",  wine_dbgstr_w(title));
297     SysFreeString(title);
298 
299     r = Folder_QueryInterface(folder, &IID_Folder2, (void **)&folder2);
300     ok(r == S_OK, "Failed to get Folder2 interface: %#x.\n", r);
301     r = Folder2_get_Self(folder2, &item);
302     ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
303     r = FolderItem_get_Path(item, &item_path);
304     ok(r == S_OK, "Failed to get item path: %#x.\n", r);
305     ok(!lstrcmpW(item_path, long_pathW), "Unexpected path %s, got %s\n", wine_dbgstr_w(item_path), wine_dbgstr_w(long_pathW));
306     SysFreeString(item_path);
307     FolderItem_Release(item);
308     Folder2_Release(folder2);
309 
310     Folder_Release(folder);
311     VariantClear(&var);
312 
313     len = lstrlenW(tempW);
314     if (len < MAX_PATH - 1)
315     {
316         lstrcatW(tempW, backslashW);
317         V_VT(&var) = VT_BSTR;
318         V_BSTR(&var) = SysAllocString(tempW);
319         r = IShellDispatch_NameSpace(sd, var, &folder);
320         ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
321         if (r == S_OK)
322         {
323             r = Folder_get_Title(folder, &title);
324             ok(r == S_OK, "Folder::get_Title failed: %08x\n", r);
325             if (r == S_OK)
326             {
327                 ok(!lstrcmpW(title, winetestW), "bad title: %s\n",
328                  wine_dbgstr_w(title));
329                 SysFreeString(title);
330             }
331             r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
332             ok(r == S_OK, "Failed to get Folder2 interface: %#x.\n", r);
333             if (r == S_OK)
334             {
335                 r = Folder2_get_Self(folder2, &item);
336                 ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
337                 if (r == S_OK)
338                 {
339                     r = FolderItem_get_Path(item, &item_path);
340                     ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
341                     ok(!lstrcmpW(item_path, long_pathW), "Unexpected path %s, got %s\n", wine_dbgstr_w(item_path),
342                         wine_dbgstr_w(long_pathW));
343                     SysFreeString(item_path);
344                     FolderItem_Release(item);
345                 }
346                 Folder2_Release(folder2);
347             }
348             Folder_Release(folder);
349         }
350         SysFreeString(V_BSTR(&var));
351     }
352 
353     heap_free(long_pathW);
354     RemoveDirectoryW(winetestW);
355     SetCurrentDirectoryW(curW);
356     IShellDispatch_Release(sd);
357 }
358 
359 static void test_items(void)
360 {
361     static const struct
362     {
363         char name[32];
364         enum
365         {
366             DIRECTORY,
367             EMPTY_FILE,
368         }
369         type;
370     }
371     file_defs[] =
372     {
373         { "00-Myfolder",        DIRECTORY  },
374         { "01-empty.bin",       EMPTY_FILE },
375     };
376     WCHAR path[MAX_PATH], cur_dir[MAX_PATH], orig_dir[MAX_PATH];
377     HRESULT r;
378     IShellDispatch *sd = NULL;
379     Folder *folder = NULL;
380     FolderItems *items;
381     FolderItems2 *items2 = NULL;
382     FolderItems3 *items3 = NULL;
383     FolderItem *item = (FolderItem*)0xdeadbeef, *item2;
384     FolderItemVerbs *verbs = (FolderItemVerbs*)0xdeadbeef;
385     VARIANT var, var2, int_index, str_index, str_index2;
386     IDispatch *disp, *disp2;
387     LONG count = -1;
388     IUnknown *unk;
389     HANDLE file;
390     BSTR bstr;
391     char cstr[64];
392     BOOL ret;
393     int i;
394 
395     r = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, &IID_IShellDispatch, (void**)&sd);
396     ok(SUCCEEDED(r), "CoCreateInstance failed: %08x\n", r);
397     ok(!!sd, "sd is null\n");
398 
399     /* create and enter a temporary directory and a folder object for it */
400     GetTempPathW(MAX_PATH, path);
401     GetCurrentDirectoryW(MAX_PATH, orig_dir);
402     SetCurrentDirectoryW(path);
403     ret = CreateDirectoryW(winetestW, NULL);
404     ok(ret, "CreateDirectory failed: %08x\n", GetLastError());
405     GetFullPathNameW(winetestW, MAX_PATH, path, NULL);
406     V_VT(&var) = VT_BSTR;
407     V_BSTR(&var) = SysAllocString(path);
408 
409     EXPECT_REF(sd, 1);
410     r = IShellDispatch_NameSpace(sd, var, &folder);
411     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
412     ok(!!folder, "folder is null\n");
413     EXPECT_REF(folder, 1);
414     EXPECT_REF(sd, 1);
415 
416     VariantClear(&var);
417     SetCurrentDirectoryW(winetestW);
418     GetCurrentDirectoryW(MAX_PATH, path);
419     GetLongPathNameW(path, cur_dir, MAX_PATH);
420 
421     /* FolderItems grabs its Folder reference */
422     items = NULL;
423     r = Folder_Items(folder, &items);
424     ok(r == S_OK, "Folder::Items failed: %08x\n", r);
425     ok(!!items, "items is null\n");
426     EXPECT_REF(folder, 2);
427     EXPECT_REF(items, 1);
428 
429     unk = NULL;
430     r = Folder_Items(folder, (FolderItems **)&unk);
431     ok(r == S_OK, "Folder::Items failed: %08x\n", r);
432     EXPECT_REF(folder, 3);
433     IUnknown_Release(unk);
434     EXPECT_REF(folder, 2);
435 
436     FolderItems_AddRef(items);
437     EXPECT_REF(folder, 2);
438     FolderItems_Release(items);
439 
440     /* Application property */
441     disp = NULL;
442     EXPECT_REF(sd, 1);
443     r = Folder_get_Application(folder, &disp);
444     ok(r == S_OK, "Failed to get application %#x.\n", r);
445     ok(disp != (IDispatch *)sd, "Unexpected application pointer\n");
446     EXPECT_REF(sd, 1);
447 
448     disp2 = NULL;
449     r = Folder_get_Application(folder, &disp2);
450     ok(r == S_OK, "Failed to get application %#x.\n", r);
451     ok(disp2 == disp, "Unexpected application pointer\n");
452     IDispatch_Release(disp2);
453 
454     r = IDispatch_QueryInterface(disp, &IID_IShellDispatch, (void **)&disp2);
455     ok(r == S_OK, "Wrong instance, hr %#x.\n", r);
456     IDispatch_Release(disp2);
457     IDispatch_Release(disp);
458 
459     if (0) /* crashes on all versions of Windows */
460         r = FolderItems_get_Count(items, NULL);
461 
462     r = FolderItems_get_Count(items, &count);
463     ok(r == S_OK, "FolderItems::get_Count failed: %08x\n", r);
464     ok(!count, "expected 0 files, got %d\n", count);
465 
466     V_VT(&var) = VT_I4;
467     V_I4(&var) = 0;
468 
469     if (0) /* crashes on all versions of Windows */
470         r = FolderItems_Item(items, var, NULL);
471 
472     r = FolderItems_Item(items, var, &item);
473     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
474     ok(!item, "item is not null\n");
475 
476     /* create test files */
477     for (i = 0; i < ARRAY_SIZE(file_defs); i++)
478     {
479         switch (file_defs[i].type)
480         {
481             case DIRECTORY:
482                 r = CreateDirectoryA(file_defs[i].name, NULL);
483                 ok(r, "CreateDirectory failed: %08x\n", GetLastError());
484                 PathCombineA(cstr, file_defs[i].name, "foo.txt");
485                 file = CreateFileA(cstr, 0, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
486                 ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %08x\n", GetLastError());
487                 CloseHandle(file);
488                 break;
489 
490             case EMPTY_FILE:
491                 file = CreateFileA(file_defs[i].name, 0, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
492                 ok(file != INVALID_HANDLE_VALUE, "CreateFile failed: %08x\n", GetLastError());
493                 CloseHandle(file);
494                 break;
495         }
496     }
497 
498     /* test that get_Count is not aware of the newly created files */
499     count = -1;
500     r = FolderItems_get_Count(items, &count);
501     ok(r == S_OK, "FolderItems::get_Count failed: %08x\n", r);
502     ok(!count, "expected 0 files, got %d\n", count);
503 
504     /* test that the newly created files CAN be retrieved by string index */
505     variant_set_string(&var, file_defs[0].name);
506     item = NULL;
507     r = FolderItems_Item(items, var, &item);
508     ok(r == S_OK, "FolderItems::Item failed: %08x\n", r);
509     ok(!!item, "item is null\n");
510 
511     disp = (void *)0xdeadbeef;
512     r = FolderItems_get_Parent(items, &disp);
513     ok(r == E_NOTIMPL, "Unexpected hr %#x.\n", r);
514     ok(disp == NULL, "Unexpected parent pointer %p.\n", disp);
515 
516     r = FolderItem_get_Parent(item, &disp);
517     ok(r == S_OK, "Failed to get parent pointer, hr %#x.\n", r);
518     ok(disp == (IDispatch *)folder, "Unexpected parent pointer %p.\n", disp);
519     IDispatch_Release(disp);
520 
521     if (item) FolderItem_Release(item);
522     VariantClear(&var);
523 
524     /* recreate the items object */
525     FolderItems_Release(items);
526     items = NULL;
527     r = Folder_Items(folder, &items);
528     ok(r == S_OK, "Folder::Items failed: %08x\n", r);
529     ok(!!items, "items is null\n");
530     r = FolderItems_QueryInterface(items, &IID_FolderItems2, (void**)&items2);
531     ok(r == S_OK || broken(r == E_NOINTERFACE) /* xp and later */, "FolderItems::QueryInterface failed: %08x\n", r);
532     if (r == S_OK)
533     {
534         ok(!!items2, "items2 is null\n");
535         FolderItems2_Release(items2);
536     }
537     r = FolderItems_QueryInterface(items, &IID_FolderItems3, (void**)&items3);
538     ok(r == S_OK, "FolderItems::QueryInterface failed: %08x\n", r);
539     ok(!!items3, "items3 is null\n");
540 
541     count = -1;
542     r = FolderItems_get_Count(items, &count);
543     ok(r == S_OK, "FolderItems::get_Count failed: %08x\n", r);
544     ok(count == ARRAY_SIZE(file_defs), "got %d files\n", count);
545 
546     /* VT_EMPTY */
547     V_VT(&var) = VT_EMPTY;
548     item = (FolderItem*)0xdeadbeef;
549     r = FolderItems_Item(items, var, &item);
550     ok(r == E_NOTIMPL, "expected E_NOTIMPL, got %08x\n", r);
551     ok(!item, "item is not null\n");
552 
553     /* VT_I2 */
554     V_VT(&var) = VT_I2;
555     V_I2(&var) = 0;
556 
557     EXPECT_REF(folder, 2);
558     EXPECT_REF(items, 2);
559     item = NULL;
560     r = FolderItems_Item(items, var, &item);
561     ok(r == S_OK, "FolderItems::Item failed: %08x\n", r);
562     ok(!!item, "item is null\n");
563     EXPECT_REF(folder, 3);
564     EXPECT_REF(items, 2);
565 
566     r = Folder_get_Application(folder, &disp);
567     ok(r == S_OK, "Failed to get application pointer %#x.\n", r);
568     r = FolderItem_get_Application(item, &disp2);
569     ok(r == S_OK, "Failed to get application pointer %#x.\n", r);
570     ok(disp == disp2, "Unexpected application pointer.\n");
571     IDispatch_Release(disp2);
572     IDispatch_Release(disp);
573 
574     FolderItem_Release(item);
575 
576     /* VT_VARIANT | VT_BYREF */
577     V_VT(&var2) = VT_I2;
578     V_I2(&var2) = 0;
579 
580     V_VT(&var) = VT_BYREF | VT_VARIANT;
581     V_VARIANTREF(&var) = &var2;
582 
583     item = NULL;
584     r = FolderItems_Item(items, var, &item);
585     ok(r == S_OK, "FolderItems::Item failed: %08x\n", r);
586     ok(!!item, "item is null\n");
587     FolderItem_Release(item);
588 
589     /* VT_I4 */
590     V_VT(&var) = VT_I4;
591     V_I4(&var) = 0;
592     item = NULL;
593     r = FolderItems_Item(items, var, &item);
594     ok(r == S_OK, "FolderItems::Item failed: %08x\n", r);
595     ok(!!item, "item is null\n");
596     if (item) FolderItem_Release(item);
597 
598     V_I4(&var) = -1;
599     item = (FolderItem*)0xdeadbeef;
600     r = FolderItems_Item(items, var, &item);
601     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
602     ok(!item, "item is not null\n");
603 
604     V_VT(&var) = VT_ERROR;
605     V_ERROR(&var) = 0;
606     item = NULL;
607     r = FolderItems_Item(items, var, &item);
608     ok(r == S_OK, "expected S_OK, got %08x\n", r);
609     ok(!!item, "item is null\n");
610     if (item)
611     {
612         bstr = NULL;
613         r = FolderItem_get_Path(item, &bstr);
614         ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
615         ok(!lstrcmpW(bstr, cur_dir),
616            "expected %s, got %s\n", wine_dbgstr_w(cur_dir), wine_dbgstr_w(bstr));
617         SysFreeString(bstr);
618         FolderItem_Release(item);
619     }
620 
621     V_VT(&int_index) = VT_I4;
622 
623     /* test the folder item corresponding to each file */
624     for (i = 0; i < ARRAY_SIZE(file_defs); i++)
625     {
626         VARIANT_BOOL b;
627         BSTR name;
628 
629         V_I4(&int_index) = i;
630         variant_set_string(&str_index, file_defs[i].name);
631 
632         item = NULL;
633         r = FolderItems_Item(items, int_index, &item);
634         ok(r == S_OK, "file_defs[%d]: FolderItems::Item failed: %08x\n", i, r);
635         ok(!!item, "file_defs[%d]: item is null\n", i);
636 
637         item2 = NULL;
638         r = FolderItems_Item(items, int_index, &item2);
639         ok(r == S_OK, "file_defs[%d]: FolderItems::Item failed: %08x\n", i, r);
640         ok(item2 != item, "file_defs[%d]: item and item2 are the same\n", i);
641         FolderItem_Release(item2);
642 
643         bstr = NULL;
644         r = FolderItem_get_Path(item, &bstr);
645         ok(r == S_OK, "file_defs[%d]: FolderItem::get_Path failed: %08x\n", i, r);
646         PathCombineW(path, cur_dir, V_BSTR(&str_index));
647         ok(!lstrcmpW(bstr, path),
648            "file_defs[%d]: expected %s, got %s\n", i, wine_dbgstr_w(path), wine_dbgstr_w(bstr));
649         SysFreeString(bstr);
650 
651         bstr = a2bstr(file_defs[i].name);
652         r = FolderItem_get_Name(item, &name);
653         ok(r == S_OK, "Failed to get item name, hr %#x.\n", r);
654         /* Returned display name does not have to strictly match file name, e.g. extension could be omitted. */
655         ok(lstrlenW(name) <= lstrlenW(bstr), "file_defs[%d]: unexpected name length.\n", i);
656         ok(!memcmp(bstr, name, lstrlenW(name) * sizeof(WCHAR)), "file_defs[%d]: unexpected name %s.\n", i, wine_dbgstr_w(name));
657         SysFreeString(name);
658         SysFreeString(bstr);
659 
660         FolderItem_Release(item);
661 
662         item = NULL;
663         r = FolderItems_Item(items, str_index, &item);
664         ok(r == S_OK, "file_defs[%d]: FolderItems::Item failed: %08x\n", i, r);
665         ok(!!item, "file_defs[%d]: item is null\n", i);
666 
667         bstr = NULL;
668         r = FolderItem_get_Path(item, &bstr);
669         ok(r == S_OK, "file_defs[%d]: FolderItem::get_Path failed: %08x\n", i, r);
670         PathCombineW(path, cur_dir, V_BSTR(&str_index));
671         ok(!lstrcmpW(bstr, path),
672            "file_defs[%d]: expected %s, got %s\n", i, wine_dbgstr_w(path), wine_dbgstr_w(bstr));
673         SysFreeString(bstr);
674 
675         b = 0xdead;
676         r = FolderItem_get_IsFolder(item, &b);
677         ok(r == S_OK, "Failed to get IsFolder property, %#x.\n", r);
678         ok(file_defs[i].type == DIRECTORY ? b == VARIANT_TRUE : b == VARIANT_FALSE, "Unexpected prop value %#x.\n", b);
679 
680         FolderItem_Release(item);
681 
682         if (file_defs[i].type == DIRECTORY)
683         {
684             /* test that getting an item object for a file in a subdirectory succeeds */
685             PathCombineA(cstr, file_defs[i].name, "foo.txt");
686             variant_set_string(&str_index2, cstr);
687             item2 = NULL;
688             r = FolderItems_Item(items, str_index2, &item2);
689             ok(r == S_OK, "file_defs[%d]: FolderItems::Item failed: %08x\n", i, r);
690             ok(!!item2, "file_defs[%d]: item is null\n", i);
691             if (item2) FolderItem_Release(item2);
692             VariantClear(&str_index2);
693 
694             /* delete the file in the subdirectory */
695             ret = DeleteFileA(cstr);
696             ok(ret, "file_defs[%d]: DeleteFile failed: %08x\n", i, GetLastError());
697 
698             /* test that getting an item object via a relative path fails */
699             strcpy(cstr, file_defs[i].name);
700             strcat(cstr, "\\..\\");
701             strcat(cstr, file_defs[i].name);
702             variant_set_string(&str_index2, cstr);
703             item2 = (FolderItem*)0xdeadbeef;
704             r = FolderItems_Item(items, str_index2, &item2);
705        todo_wine {
706             ok(r == S_FALSE, "file_defs[%d]: expected S_FALSE, got %08x\n", i, r);
707             ok(!item2, "file_defs[%d]: item is not null\n", i);
708        }
709             if (item2) FolderItem_Release(item2);
710             VariantClear(&str_index2);
711 
712             /* remove the directory */
713             ret = RemoveDirectoryA(file_defs[i].name);
714             ok(ret, "file_defs[%d]: RemoveDirectory failed: %08x\n", i, GetLastError());
715         }
716         else
717         {
718             ret = DeleteFileA(file_defs[i].name);
719             ok(ret, "file_defs[%d]: DeleteFile failed: %08x\n", i, GetLastError());
720         }
721 
722         /* test that the folder item is still accessible by integer index */
723         item = NULL;
724         r = FolderItems_Item(items, int_index, &item);
725         ok(r == S_OK, "file_defs[%d]: FolderItems::Item failed: %08x\n", i, r);
726         ok(!!item, "file_defs[%d]: item is null\n", i);
727 
728         bstr = NULL;
729         r = FolderItem_get_Path(item, &bstr);
730         ok(r == S_OK, "file_defs[%d]: FolderItem::get_Path failed: %08x\n", i, r);
731         PathCombineW(path, cur_dir, V_BSTR(&str_index));
732         ok(!lstrcmpW(bstr, path),
733            "file_defs[%d]: expected %s, got %s\n", i, wine_dbgstr_w(path), wine_dbgstr_w(bstr));
734         SysFreeString(bstr);
735 
736         FolderItem_Release(item);
737 
738         /* test that the folder item is no longer accessible by string index */
739         item = (FolderItem*)0xdeadbeef;
740         r = FolderItems_Item(items, str_index, &item);
741         ok(r == S_FALSE, "file_defs[%d]: expected S_FALSE, got %08x\n", i, r);
742         ok(!item, "file_defs[%d]: item is not null\n", i);
743 
744         VariantClear(&str_index);
745     }
746 
747     /* test that there are only as many folder items as there were files */
748     V_I4(&int_index) = ARRAY_SIZE(file_defs);
749     item = (FolderItem*)0xdeadbeef;
750     r = FolderItems_Item(items, int_index, &item);
751     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
752     ok(!item, "item is not null\n");
753 
754     if (0) /* crashes on xp */
755     {
756         r = FolderItems_get_Application(items, NULL);
757         ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
758     }
759 
760     r = FolderItems_get_Application(items, &disp);
761     ok(r == S_OK, "FolderItems::get_Application failed: %08x\n", r);
762 
763     r = Folder_get_Application(folder, &disp2);
764     ok(r == S_OK, "Failed to get application pointer, hr %#x.\n", r);
765     ok(disp == disp2, "Unexpected application pointer.\n");
766     IDispatch_Release(disp2);
767     IDispatch_Release(disp);
768 
769     if (0) /* crashes on xp */
770     {
771         r = FolderItems_get_Parent(items, NULL);
772         ok(r == E_NOTIMPL, "expected E_NOTIMPL, got %08x\n", r);
773     }
774 
775     disp = (IDispatch*)0xdeadbeef;
776     r = FolderItems_get_Parent(items, &disp);
777     ok(r == E_NOTIMPL, "expected E_NOTIMPL, got %08x\n", r);
778     ok(!disp, "disp is not null\n");
779 
780     if (0) /* crashes on xp */
781     {
782         r = FolderItems__NewEnum(items, NULL);
783         ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
784     }
785 
786     r = FolderItems__NewEnum(items, &unk);
787 todo_wine
788     ok(r == S_OK, "FolderItems::_NewEnum failed: %08x\n", r);
789 todo_wine
790     ok(!!unk, "unk is null\n");
791     if (unk) IUnknown_Release(unk);
792 
793     if (items3)
794     {
795         r = FolderItems3_Filter(items3, 0, NULL);
796 todo_wine
797         ok(r == S_OK, "expected S_OK, got %08x\n", r);
798 
799         if (0) /* crashes on xp */
800         {
801             r = FolderItems3_get_Verbs(items3, NULL);
802             ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
803         }
804 
805         r = FolderItems3_get_Verbs(items3, &verbs);
806 todo_wine
807         ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
808         ok(!verbs, "verbs is not null\n");
809     }
810 
811     /* remove the temporary directory and restore the original working directory */
812     GetTempPathW(MAX_PATH, path);
813     SetCurrentDirectoryW(path);
814     ret = RemoveDirectoryW(winetestW);
815     ok(ret, "RemoveDirectory failed: %08x\n", GetLastError());
816     SetCurrentDirectoryW(orig_dir);
817 
818     /* test that everything stops working after the directory has been removed */
819     count = -1;
820     r = FolderItems_get_Count(items, &count);
821     ok(r == S_OK, "FolderItems::get_Count failed: %08x\n", r);
822     ok(!count, "expected 0 files, got %d\n", count);
823 
824     item = NULL;
825     V_I4(&int_index) = 0;
826     item = (FolderItem*)0xdeadbeef;
827     r = FolderItems_Item(items, int_index, &item);
828     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
829     ok(!item, "item is not null\n");
830 
831     variant_set_string(&str_index, file_defs[0].name);
832     item = (FolderItem*)0xdeadbeef;
833     r = FolderItems_Item(items, str_index, &item);
834     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
835     ok(!item, "item is not null\n");
836     VariantClear(&str_index);
837 
838     FolderItems_Release(items);
839     Folder_Release(folder);
840     if (items3) FolderItems3_Release(items3);
841     IShellDispatch_Release(sd);
842 }
843 
844 static void test_service(void)
845 {
846     static const WCHAR spooler[] = {'S','p','o','o','l','e','r',0};
847     static const WCHAR dummyW[] = {'d','u','m','m','y',0};
848     SERVICE_STATUS_PROCESS status;
849     SC_HANDLE scm, service;
850     IShellDispatch2 *sd;
851     DWORD dummy;
852     HRESULT hr;
853     BSTR name;
854     VARIANT v;
855 
856     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
857         &IID_IShellDispatch2, (void**)&sd);
858     if (hr != S_OK)
859     {
860         win_skip("IShellDispatch2 not supported\n");
861         return;
862     }
863 
864     V_VT(&v) = VT_I2;
865     V_I2(&v) = 10;
866     hr = IShellDispatch2_IsServiceRunning(sd, NULL, &v);
867     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
868     ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
869     EXPECT_HR(hr, S_OK);
870 
871     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
872     service = OpenServiceW(scm, spooler, SERVICE_QUERY_STATUS);
873     QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy);
874     CloseServiceHandle(service);
875     CloseServiceHandle(scm);
876 
877     /* service should exist */
878     name = SysAllocString(spooler);
879     V_VT(&v) = VT_I2;
880     hr = IShellDispatch2_IsServiceRunning(sd, name, &v);
881     EXPECT_HR(hr, S_OK);
882     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
883     if (status.dwCurrentState == SERVICE_RUNNING)
884         ok(V_BOOL(&v) == VARIANT_TRUE, "got %d\n", V_BOOL(&v));
885     else
886         ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
887     SysFreeString(name);
888 
889     /* service doesn't exist */
890     name = SysAllocString(dummyW);
891     V_VT(&v) = VT_I2;
892     hr = IShellDispatch2_IsServiceRunning(sd, name, &v);
893     EXPECT_HR(hr, S_OK);
894     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
895     ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
896     SysFreeString(name);
897 
898     IShellDispatch2_Release(sd);
899 }
900 
901 static void test_dispatch_typeinfo(IDispatch *disp, REFIID *riid)
902 {
903     ITypeInfo *typeinfo;
904     TYPEATTR *typeattr;
905     UINT count;
906     HRESULT hr;
907 
908     count = 10;
909     hr = IDispatch_GetTypeInfoCount(disp, &count);
910     ok(hr == S_OK, "got 0x%08x\n", hr);
911     ok(count == 1, "got %u\n", count);
912 
913     hr = IDispatch_GetTypeInfo(disp, 0, LOCALE_SYSTEM_DEFAULT, &typeinfo);
914     ok(hr == S_OK, "got 0x%08x\n", hr);
915 
916     hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr);
917     ok(hr == S_OK, "got 0x%08x\n", hr);
918     while (!IsEqualGUID(*riid, &IID_NULL)) {
919         if (IsEqualGUID(&typeattr->guid, *riid))
920             break;
921         riid++;
922     }
923     ok(IsEqualGUID(&typeattr->guid, *riid), "unexpected type guid %s\n", wine_dbgstr_guid(&typeattr->guid));
924 
925     ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr);
926     ITypeInfo_Release(typeinfo);
927 }
928 
929 static void test_ShellFolderViewDual(void)
930 {
931     static const IID *shelldisp_riids[] = {
932         &IID_IShellDispatch6,
933         &IID_IShellDispatch5,
934         &IID_IShellDispatch4,
935         &IID_IShellDispatch2,
936         &IID_IWin7ShellDispatch6,
937         &IID_NULL
938     };
939     IShellFolderViewDual *viewdual;
940     IShellFolder *desktop, *tmpdir;
941     IShellView *view, *view2;
942     IDispatch *disp, *disp2;
943     WCHAR pathW[MAX_PATH];
944     LPITEMIDLIST pidl;
945     HRESULT hr;
946 
947     /* IShellFolderViewDual is not an IShellView extension */
948     hr = SHGetDesktopFolder(&desktop);
949     ok(hr == S_OK, "got 0x%08x\n", hr);
950 
951     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
952     ok(hr == S_OK, "got 0x%08x\n", hr);
953 
954     hr = IShellView_QueryInterface(view, &IID_IShellFolderViewDual, (void**)&viewdual);
955     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
956 
957     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp);
958     ok(hr == S_OK, "got 0x%08x\n", hr);
959 
960     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp2);
961     ok(hr == S_OK, "got 0x%08x\n", hr);
962     ok(disp2 == disp, "got %p, %p\n", disp2, disp);
963     IDispatch_Release(disp2);
964 
965     hr = IDispatch_QueryInterface(disp, &IID_IShellFolderViewDual, (void**)&viewdual);
966     ok(hr == S_OK, "got 0x%08x\n", hr);
967     ok(disp == (IDispatch*)viewdual, "got %p, expected %p\n", viewdual, disp);
968 
969     hr = IShellFolderViewDual_QueryInterface(viewdual, &IID_IShellView, (void**)&view2);
970     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
971 
972     /* get_Application() */
973 
974 if (0) /* crashes on pre-vista */ {
975     hr = IShellFolderViewDual_get_Application(viewdual, NULL);
976     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
977 }
978     hr = IShellFolderViewDual_get_Application(viewdual, &disp2);
979     ok(hr == S_OK, "got 0x%08x\n", hr);
980     ok(disp2 != (IDispatch*)viewdual, "got %p, %p\n", disp2, viewdual);
981     test_dispatch_typeinfo(disp2, shelldisp_riids);
982     IDispatch_Release(disp2);
983 
984     IShellFolderViewDual_Release(viewdual);
985     IDispatch_Release(disp);
986 
987     disp = (void*)0xdeadbeef;
988     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IShellFolderViewDual, (void**)&disp);
989     ok(hr == E_NOINTERFACE || broken(hr == E_NOTIMPL) /* win2k */, "got 0x%08x\n", hr);
990     ok(disp == NULL, "got %p\n", disp);
991     IShellView_Release(view);
992 
993     /* Try with some other folder, that's not a desktop */
994     GetTempPathW(ARRAY_SIZE(pathW), pathW);
995     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, pathW, NULL, &pidl, NULL);
996     ok(hr == S_OK, "got 0x%08x\n", hr);
997 
998     hr = IShellFolder_BindToObject(desktop, pidl, NULL, &IID_IShellFolder, (void**)&tmpdir);
999     ok(hr == S_OK, "got 0x%08x\n", hr);
1000     CoTaskMemFree(pidl);
1001 
1002     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
1003     ok(hr == S_OK, "got 0x%08x\n", hr);
1004 
1005     hr = IShellView_QueryInterface(view, &IID_IShellFolderViewDual, (void**)&viewdual);
1006     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1007 
1008     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp);
1009     ok(hr == S_OK, "got 0x%08x\n", hr);
1010     IDispatch_Release(disp);
1011     IShellView_Release(view);
1012 
1013     IShellFolder_Release(tmpdir);
1014     IShellFolder_Release(desktop);
1015 }
1016 
1017 static void test_ShellWindows(void)
1018 {
1019     IShellWindows *shellwindows;
1020     LONG cookie, cookie2, ret;
1021     IDispatch *disp;
1022     VARIANT v, v2;
1023     HRESULT hr;
1024     HWND hwnd;
1025 
1026     hr = CoCreateInstance(&CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER,
1027         &IID_IShellWindows, (void**)&shellwindows);
1028     ok(hr == S_OK, "got 0x%08x\n", hr);
1029     /* TODO: remove when explorer startup with clean prefix is fixed */
1030     if (hr != S_OK)
1031         return;
1032 
1033     hr = IShellWindows_Register(shellwindows, NULL, 0, SWC_EXPLORER, NULL);
1034     ok(hr == HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), "got 0x%08x\n", hr);
1035 
1036     hr = IShellWindows_Register(shellwindows, NULL, 0, SWC_EXPLORER, &cookie);
1037 todo_wine
1038     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1039 
1040     hr = IShellWindows_Register(shellwindows, (IDispatch*)shellwindows, 0, SWC_EXPLORER, &cookie);
1041 todo_wine
1042     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1043 
1044     hr = IShellWindows_Register(shellwindows, (IDispatch*)shellwindows, 0, SWC_EXPLORER, &cookie);
1045 todo_wine
1046     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1047 
1048     hwnd = CreateWindowExA(0, "button", "test", BS_CHECKBOX | WS_VISIBLE | WS_POPUP,
1049                            0, 0, 50, 14, 0, 0, 0, NULL);
1050     ok(hwnd != NULL, "got %p, error %d\n", hwnd, GetLastError());
1051 
1052     cookie = 0;
1053     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_EXPLORER, &cookie);
1054 todo_wine {
1055     ok(hr == S_OK, "got 0x%08x\n", hr);
1056     ok(cookie != 0, "got %d\n", cookie);
1057 }
1058     cookie2 = 0;
1059     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_EXPLORER, &cookie2);
1060 todo_wine {
1061     ok(hr == S_OK, "got 0x%08x\n", hr);
1062     ok(cookie2 != 0 && cookie2 != cookie, "got %d\n", cookie2);
1063 }
1064     hr = IShellWindows_Revoke(shellwindows, cookie);
1065 todo_wine
1066     ok(hr == S_OK, "got 0x%08x\n", hr);
1067     hr = IShellWindows_Revoke(shellwindows, cookie2);
1068 todo_wine
1069     ok(hr == S_OK, "got 0x%08x\n", hr);
1070 
1071     hr = IShellWindows_Revoke(shellwindows, 0);
1072 todo_wine
1073     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1074 
1075     /* we can register ourselves as desktop, but FindWindowSW still returns real desktop window */
1076     cookie = 0;
1077     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_DESKTOP, &cookie);
1078 todo_wine {
1079     ok(hr == S_OK, "got 0x%08x\n", hr);
1080     ok(cookie != 0, "got %d\n", cookie);
1081 }
1082     disp = (void*)0xdeadbeef;
1083     ret = 0xdead;
1084     VariantInit(&v);
1085     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v, SWC_DESKTOP, &ret, SWFO_NEEDDISPATCH, &disp);
1086     ok(hr == S_OK || broken(hr == S_FALSE), "got 0x%08x\n", hr);
1087     if (hr == S_FALSE) /* winxp and earlier */ {
1088         win_skip("SWC_DESKTOP is not supported, some tests will be skipped.\n");
1089         /* older versions allowed to register SWC_DESKTOP and access it with FindWindowSW */
1090         ok(disp == NULL, "got %p\n", disp);
1091         ok(ret == 0, "got %d\n", ret);
1092     }
1093     else {
1094         static const IID *browser_riids[] = {
1095             &IID_IWebBrowser2,
1096             &IID_NULL
1097         };
1098 
1099         static const IID *viewdual_riids[] = {
1100             &IID_IShellFolderViewDual3,
1101             &IID_NULL
1102         };
1103 
1104         IShellFolderViewDual *view;
1105         IShellBrowser *sb, *sb2;
1106         IServiceProvider *sp;
1107         IDispatch *doc, *app;
1108         IWebBrowser2 *wb;
1109         IShellView *sv;
1110         IUnknown *unk;
1111 
1112         ok(disp != NULL, "got %p\n", disp);
1113         ok(ret != HandleToUlong(hwnd), "got %d\n", ret);
1114 
1115         /* IDispatch-related tests */
1116         test_dispatch_typeinfo(disp, browser_riids);
1117 
1118         /* IWebBrowser2 */
1119         hr = IDispatch_QueryInterface(disp, &IID_IWebBrowser2, (void**)&wb);
1120         ok(hr == S_OK, "got 0x%08x\n", hr);
1121 
1122         hr = IWebBrowser2_Refresh(wb);
1123 todo_wine
1124         ok(hr == S_OK, "got 0x%08x\n", hr);
1125 
1126         hr = IWebBrowser2_get_Application(wb, &app);
1127         ok(hr == S_OK, "got 0x%08x\n", hr);
1128         ok(disp == app, "got %p, %p\n", app, disp);
1129         IDispatch_Release(app);
1130 
1131         hr = IWebBrowser2_get_Document(wb, &doc);
1132 todo_wine
1133         ok(hr == S_OK, "got 0x%08x\n", hr);
1134 if (hr == S_OK) {
1135         test_dispatch_typeinfo(doc, viewdual_riids);
1136 }
1137         IWebBrowser2_Release(wb);
1138 
1139         /* IServiceProvider */
1140         hr = IDispatch_QueryInterface(disp, &IID_IShellFolderViewDual, (void**)&view);
1141         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1142 
1143         hr = IDispatch_QueryInterface(disp, &IID_IServiceProvider, (void**)&sp);
1144         ok(hr == S_OK, "got 0x%08x\n", hr);
1145 
1146         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellBrowser, (void**)&sb);
1147         ok(hr == S_OK, "got 0x%08x\n", hr);
1148 
1149         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellBrowser, (void**)&sb2);
1150         ok(hr == S_OK, "got 0x%08x\n", hr);
1151         ok(sb == sb2, "got %p, %p\n", sb, sb2);
1152 
1153         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IOleWindow, (void**)&unk);
1154         ok(hr == S_OK, "got 0x%08x\n", hr);
1155         IUnknown_Release(unk);
1156 
1157         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IExplorerBrowser, (void**)&unk);
1158         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1159 
1160         hr = IShellBrowser_QueryInterface(sb, &IID_IExplorerBrowser, (void**)&unk);
1161         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1162 
1163         hr = IShellBrowser_QueryInterface(sb, &IID_IWebBrowser2, (void**)&unk);
1164         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1165 
1166         hr = IShellBrowser_QueryInterface(sb, &IID_IDispatch, (void**)&unk);
1167         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1168 
1169         hr = IShellBrowser_QueryActiveShellView(sb, &sv);
1170         ok(hr == S_OK, "got 0x%08x\n", hr);
1171         IShellView_Release(sv);
1172 
1173         IShellBrowser_Release(sb2);
1174         IShellBrowser_Release(sb);
1175 
1176         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IUnknown, (void**)&unk);
1177         ok(hr == S_OK, "got 0x%08x\n", hr);
1178 
1179         hr = IUnknown_QueryInterface(unk, &IID_IShellBrowser, (void**)&sb2);
1180         ok(hr == S_OK, "got 0x%08x\n", hr);
1181         IShellBrowser_Release(sb2);
1182         IUnknown_Release(unk);
1183 
1184         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellView, (void**)&sv);
1185         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1186 
1187         IServiceProvider_Release(sp);
1188         IDispatch_Release(disp);
1189     }
1190 
1191     disp = (void*)0xdeadbeef;
1192     ret = 0xdead;
1193     VariantInit(&v);
1194     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v, SWC_DESKTOP, &ret, 0, &disp);
1195     ok(hr == S_OK || broken(hr == S_FALSE) /* winxp */, "got 0x%08x\n", hr);
1196     ok(disp == NULL, "got %p\n", disp);
1197     ok(ret != HandleToUlong(hwnd), "got %d\n", ret);
1198 
1199     disp = (void*)0xdeadbeef;
1200     ret = 0xdead;
1201     V_VT(&v) = VT_I4;
1202     V_I4(&v) = cookie;
1203     VariantInit(&v2);
1204     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v2, SWC_BROWSER, &ret, SWFO_COOKIEPASSED, &disp);
1205 todo_wine
1206     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1207     ok(disp == NULL, "got %p\n", disp);
1208     ok(ret == 0, "got %d\n", ret);
1209 
1210     hr = IShellWindows_Revoke(shellwindows, cookie);
1211 todo_wine
1212     ok(hr == S_OK, "got 0x%08x\n", hr);
1213     DestroyWindow(hwnd);
1214     IShellWindows_Release(shellwindows);
1215 }
1216 
1217 static void test_ParseName(void)
1218 {
1219     static const WCHAR cadabraW[] = {'c','a','d','a','b','r','a',0};
1220     WCHAR pathW[MAX_PATH];
1221     IShellDispatch *sd;
1222     FolderItem *item;
1223     Folder *folder;
1224     HRESULT hr;
1225     VARIANT v;
1226     BSTR str;
1227 
1228     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
1229         &IID_IShellDispatch, (void**)&sd);
1230     ok(hr == S_OK, "got 0x%08x\n", hr);
1231 
1232     GetTempPathW(ARRAY_SIZE(pathW), pathW);
1233     V_VT(&v) = VT_BSTR;
1234     V_BSTR(&v) = SysAllocString(pathW);
1235     hr = IShellDispatch_NameSpace(sd, v, &folder);
1236     ok(hr == S_OK, "got 0x%08x\n", hr);
1237     VariantClear(&v);
1238 
1239     item = (void*)0xdeadbeef;
1240     hr = Folder_ParseName(folder, NULL, &item);
1241     ok(hr == S_FALSE || broken(hr == E_INVALIDARG) /* win2k */, "got 0x%08x\n", hr);
1242     ok(item == NULL, "got %p\n", item);
1243 
1244     /* empty name */
1245     str = SysAllocStringLen(NULL, 0);
1246     item = (void*)0xdeadbeef;
1247     hr = Folder_ParseName(folder, str, &item);
1248     ok(hr == S_FALSE || broken(hr == E_INVALIDARG) /* win2k */, "got 0x%08x\n", hr);
1249     ok(item == NULL, "got %p\n", item);
1250     SysFreeString(str);
1251 
1252     /* path doesn't exist */
1253     str = SysAllocString(cadabraW);
1254     item = (void*)0xdeadbeef;
1255     hr = Folder_ParseName(folder, str, &item);
1256     ok(hr == S_FALSE || broken(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) /* win2k */,
1257         "got 0x%08x\n", hr);
1258     ok(item == NULL, "got %p\n", item);
1259     SysFreeString(str);
1260 
1261     lstrcatW(pathW, cadabraW);
1262     CreateDirectoryW(pathW, NULL);
1263 
1264     str = SysAllocString(cadabraW);
1265     item = NULL;
1266     hr = Folder_ParseName(folder, str, &item);
1267     ok(hr == S_OK, "got 0x%08x\n", hr);
1268     ok(item != NULL, "got %p\n", item);
1269     SysFreeString(str);
1270 
1271     hr = FolderItem_get_Path(item, &str);
1272     ok(hr == S_OK, "got 0x%08x\n", hr);
1273     ok(str[0] != 0, "path %s\n", wine_dbgstr_w(str));
1274     SysFreeString(str);
1275 
1276     RemoveDirectoryW(pathW);
1277     FolderItem_Release(item);
1278     Folder_Release(folder);
1279     IShellDispatch_Release(sd);
1280 }
1281 
1282 static void test_Verbs(void)
1283 {
1284     FolderItemVerbs *verbs, *verbs2;
1285     WCHAR pathW[MAX_PATH];
1286     FolderItemVerb *verb;
1287     IShellDispatch *sd;
1288     FolderItem *item;
1289     Folder2 *folder2;
1290     IDispatch *disp;
1291     Folder *folder;
1292     HRESULT hr;
1293     LONG count, i;
1294     VARIANT v;
1295     BSTR str;
1296 
1297     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
1298         &IID_IShellDispatch, (void**)&sd);
1299     ok(hr == S_OK, "got 0x%08x\n", hr);
1300 
1301     GetTempPathW(ARRAY_SIZE(pathW), pathW);
1302     V_VT(&v) = VT_BSTR;
1303     V_BSTR(&v) = SysAllocString(pathW);
1304     hr = IShellDispatch_NameSpace(sd, v, &folder);
1305     ok(hr == S_OK, "got 0x%08x\n", hr);
1306     VariantClear(&v);
1307 
1308     hr = Folder_QueryInterface(folder, &IID_Folder2, (void**)&folder2);
1309     ok(hr == S_OK, "got 0x%08x\n", hr);
1310     Folder_Release(folder);
1311 
1312     hr = Folder2_get_Self(folder2, &item);
1313     ok(hr == S_OK, "got 0x%08x\n", hr);
1314     Folder2_Release(folder2);
1315 
1316 if (0) { /* crashes on some systems */
1317     hr = FolderItem_Verbs(item, NULL);
1318     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1319 }
1320     hr = FolderItem_Verbs(item, &verbs);
1321     ok(hr == S_OK, "got 0x%08x\n", hr);
1322 
1323     hr = FolderItem_Verbs(item, &verbs2);
1324     ok(hr == S_OK, "got 0x%08x\n", hr);
1325     ok(verbs2 != verbs, "Unexpected verbs pointer.\n");
1326     FolderItemVerbs_Release(verbs2);
1327 
1328     disp = (void *)0xdeadbeef;
1329     hr = FolderItemVerbs_get_Application(verbs, &disp);
1330     ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr);
1331     ok(disp == NULL, "Unexpected application pointer.\n");
1332 
1333     disp = (void *)0xdeadbeef;
1334     hr = FolderItemVerbs_get_Parent(verbs, &disp);
1335     ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr);
1336     ok(disp == NULL, "Unexpected parent pointer %p.\n", disp);
1337 
1338 if (0) { /* crashes on winxp/win2k3 */
1339     hr = FolderItemVerbs_get_Count(verbs, NULL);
1340     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1341 }
1342     count = 0;
1343     hr = FolderItemVerbs_get_Count(verbs, &count);
1344     ok(hr == S_OK, "got 0x%08x\n", hr);
1345     ok(count > 0, "got count %d\n", count);
1346 
1347 if (0) { /* crashes on winxp/win2k3 */
1348     V_VT(&v) = VT_I4;
1349     V_I4(&v) = 0;
1350     hr = FolderItemVerbs_Item(verbs, v, NULL);
1351     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1352 }
1353     /* there's always one item more, so you can access [0,count],
1354        instead of actual [0,count) */
1355     for (i = 0; i <= count; i++) {
1356         V_VT(&v) = VT_I4;
1357         V_I4(&v) = i;
1358         hr = FolderItemVerbs_Item(verbs, v, &verb);
1359         ok(hr == S_OK, "got 0x%08x\n", hr);
1360         hr = FolderItemVerb_get_Name(verb, &str);
1361         ok(hr == S_OK, "got 0x%08x\n", hr);
1362         ok(str != NULL, "%d: name %s\n", i, wine_dbgstr_w(str));
1363         if (i == count)
1364             ok(str[0] == 0, "%d: got terminating item %s\n", i, wine_dbgstr_w(str));
1365 
1366         disp = (void *)0xdeadbeef;
1367         hr = FolderItemVerb_get_Parent(verb, &disp);
1368         ok(hr == E_NOTIMPL, "got %#x.\n", hr);
1369         ok(disp == NULL, "Unexpected parent pointer %p.\n", disp);
1370 
1371         disp = (void *)0xdeadbeef;
1372         hr = FolderItemVerb_get_Application(verb, &disp);
1373         ok(hr == E_NOTIMPL, "got %#x.\n", hr);
1374         ok(disp == NULL, "Unexpected parent pointer %p.\n", disp);
1375 
1376         SysFreeString(str);
1377         FolderItemVerb_Release(verb);
1378     }
1379 
1380     V_VT(&v) = VT_I4;
1381     V_I4(&v) = count+1;
1382     verb = NULL;
1383     hr = FolderItemVerbs_Item(verbs, v, &verb);
1384     ok(hr == S_OK, "got 0x%08x\n", hr);
1385     ok(verb == NULL, "got %p\n", verb);
1386 
1387     FolderItemVerbs_Release(verbs);
1388     FolderItem_Release(item);
1389     IShellDispatch_Release(sd);
1390 }
1391 
1392 static void test_ShellExecute(void)
1393 {
1394     HRESULT hr;
1395     IShellDispatch2 *sd;
1396     BSTR name;
1397     VARIANT args, dir, op, show;
1398 
1399     static const WCHAR regW[] = {'r','e','g',0};
1400 
1401     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
1402         &IID_IShellDispatch2, (void**)&sd);
1403     if (hr != S_OK)
1404     {
1405         win_skip("IShellDispatch2 not supported\n");
1406         return;
1407     }
1408 
1409     VariantInit(&args);
1410     VariantInit(&dir);
1411     VariantInit(&op);
1412     VariantInit(&show);
1413 
1414     V_VT(&show) = VT_I4;
1415     V_I4(&show) = 0;
1416 
1417     name = SysAllocString(regW);
1418 
1419     hr = IShellDispatch2_ShellExecute(sd, name, args, dir, op, show);
1420     ok(hr == S_OK, "ShellExecute failed: %08x\n", hr);
1421 
1422     /* test invalid value for show */
1423     V_VT(&show) = VT_BSTR;
1424     V_BSTR(&show) = name;
1425 
1426     hr = IShellDispatch2_ShellExecute(sd, name, args, dir, op, show);
1427     ok(hr == S_OK, "ShellExecute failed: %08x\n", hr);
1428 
1429     SysFreeString(name);
1430     IShellDispatch2_Release(sd);
1431 }
1432 
1433 START_TEST(shelldispatch)
1434 {
1435     HRESULT r;
1436 
1437     r = CoInitialize(NULL);
1438     ok(SUCCEEDED(r), "CoInitialize failed: %08x\n", r);
1439     if (FAILED(r))
1440         return;
1441 
1442     init_function_pointers();
1443     test_namespace();
1444     test_items();
1445     test_service();
1446     test_ShellFolderViewDual();
1447     test_ShellWindows();
1448     test_ParseName();
1449     test_Verbs();
1450     test_ShellExecute();
1451 
1452     CoUninitialize();
1453 }
1454