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 #include "precomp.h"
22 
23 #include <winsvc.h>
24 #include <initguid.h>
25 
26 #define EXPECT_HR(hr,hr_exp) \
27     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
28 
29 static const WCHAR winetestW[] = {'w','i','n','e','t','e','s','t',0};
30 
31 static HRESULT (WINAPI *pSHGetFolderPathW)(HWND, int, HANDLE, DWORD, LPWSTR);
32 static HRESULT (WINAPI *pSHGetNameFromIDList)(PCIDLIST_ABSOLUTE,SIGDN,PWSTR*);
33 static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
34 static DWORD (WINAPI *pGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD);
35 
36 /* Updated Windows 7 has a new IShellDispatch6 in its typelib */
37 DEFINE_GUID(IID_IWin7ShellDispatch6, 0x34936ba1, 0x67ad, 0x4c41, 0x99,0xb8, 0x8c,0x12,0xdf,0xf1,0xe9,0x74);
38 
39 static void init_function_pointers(void)
40 {
41     HMODULE hshell32, hkernel32;
42 
43     hshell32 = GetModuleHandleA("shell32.dll");
44     hkernel32 = GetModuleHandleA("kernel32.dll");
45     pSHGetFolderPathW = (void*)GetProcAddress(hshell32, "SHGetFolderPathW");
46     pSHGetNameFromIDList = (void*)GetProcAddress(hshell32, "SHGetNameFromIDList");
47     pSHGetSpecialFolderLocation = (void*)GetProcAddress(hshell32,
48      "SHGetSpecialFolderLocation");
49     pGetLongPathNameW = (void*)GetProcAddress(hkernel32, "GetLongPathNameW");
50 }
51 
52 static void test_namespace(void)
53 {
54     static const ShellSpecialFolderConstants special_folders[] =
55     {
56         ssfDESKTOP,
57         ssfPROGRAMS,
58         ssfCONTROLS,
59         ssfPRINTERS,
60         ssfPERSONAL,
61         ssfFAVORITES,
62         ssfSTARTUP,
63         ssfRECENT,
64         ssfSENDTO,
65         ssfBITBUCKET,
66         ssfSTARTMENU,
67         ssfDESKTOPDIRECTORY,
68         ssfDRIVES,
69         ssfNETWORK,
70         ssfNETHOOD,
71         ssfFONTS,
72         ssfTEMPLATES,
73         ssfCOMMONSTARTMENU,
74         ssfCOMMONPROGRAMS,
75         ssfCOMMONSTARTUP,
76         ssfCOMMONDESKTOPDIR,
77         ssfAPPDATA,
78         ssfPRINTHOOD,
79         ssfLOCALAPPDATA,
80         ssfALTSTARTUP,
81         ssfCOMMONALTSTARTUP,
82         ssfCOMMONFAVORITES,
83         ssfINTERNETCACHE,
84         ssfCOOKIES,
85         ssfHISTORY,
86         ssfCOMMONAPPDATA,
87         ssfWINDOWS,
88         ssfSYSTEM,
89         ssfPROGRAMFILES,
90         ssfMYPICTURES,
91         ssfPROFILE,
92         ssfSYSTEMx86,
93         ssfPROGRAMFILESx86,
94     };
95 
96     static const WCHAR backslashW[] = {'\\',0};
97     static const WCHAR clsidW[] = {
98         ':',':','{','6','4','5','F','F','0','4','0','-','5','0','8','1','-',
99                     '1','0','1','B','-','9','F','0','8','-',
100                     '0','0','A','A','0','0','2','F','9','5','4','E','}',0};
101 
102     static WCHAR tempW[MAX_PATH], curW[MAX_PATH];
103     WCHAR *long_pathW = NULL;
104     HRESULT r;
105     IShellDispatch *sd;
106     Folder *folder;
107     Folder2 *folder2;
108     FolderItem *item;
109     VARIANT var;
110     BSTR title, item_path;
111     int len, i;
112 
113     r = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
114      &IID_IShellDispatch, (LPVOID*)&sd);
115     if (r == REGDB_E_CLASSNOTREG) /* NT4 */
116     {
117         win_skip("skipping IShellDispatch tests\n");
118         return;
119     }
120     ok(SUCCEEDED(r), "CoCreateInstance failed: %08x\n", r);
121     if (FAILED(r))
122         return;
123 
124     VariantInit(&var);
125     folder = (void*)0xdeadbeef;
126     r = IShellDispatch_NameSpace(sd, var, &folder);
127     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
128     ok(folder == NULL, "expected NULL, got %p\n", folder);
129 
130     /* test valid folder ids */
131     for (i = 0; i < sizeof(special_folders)/sizeof(special_folders[0]); i++)
132     {
133         V_VT(&var) = VT_I4;
134         V_I4(&var) = special_folders[i];
135         folder = (void*)0xdeadbeef;
136         r = IShellDispatch_NameSpace(sd, var, &folder);
137         if (special_folders[i] == ssfALTSTARTUP || special_folders[i] == ssfCOMMONALTSTARTUP)
138             ok(r == S_OK || broken(r == S_FALSE) /* winxp */, "Failed to get folder for index %#x, got %08x\n", special_folders[i], r);
139         else
140             ok(r == S_OK, "Failed to get folder for index %#x, got %08x\n", special_folders[i], r);
141         if (folder)
142             Folder_Release(folder);
143     }
144 
145     V_VT(&var) = VT_I4;
146     V_I4(&var) = -1;
147     folder = (void*)0xdeadbeef;
148     r = IShellDispatch_NameSpace(sd, var, &folder);
149     todo_wine {
150     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
151     ok(folder == NULL, "got %p\n", folder);
152     if (r == S_OK)
153         Folder_Release(folder);
154 }
155     V_VT(&var) = VT_I4;
156     V_I4(&var) = ssfPROGRAMFILES;
157     r = IShellDispatch_NameSpace(sd, var, &folder);
158     ok(r == S_OK ||
159      broken(r == S_FALSE), /* NT4 */
160      "IShellDispatch::NameSpace failed: %08x\n", r);
161     if (r == S_OK)
162     {
163         static WCHAR path[MAX_PATH];
164 
165         if (pSHGetFolderPathW)
166         {
167             r = pSHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL,
168              SHGFP_TYPE_CURRENT, path);
169             ok(r == S_OK, "SHGetFolderPath failed: %08x\n", r);
170         }
171         r = Folder_get_Title(folder, &title);
172         todo_wine
173         ok(r == S_OK, "Folder::get_Title failed: %08x\n", r);
174         if (r == S_OK)
175         {
176             /* On Win2000-2003 title is equal to program files directory name in
177                HKLM\Software\Microsoft\Windows\CurrentVersion\ProgramFilesDir.
178                On newer Windows it seems constant and is not changed
179                if the program files directory name is changed */
180             if (pSHGetSpecialFolderLocation && pSHGetNameFromIDList)
181             {
182                 LPITEMIDLIST pidl;
183                 PWSTR name;
184 
185                 r = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl);
186                 ok(r == S_OK, "SHGetSpecialFolderLocation failed: %08x\n", r);
187                 r = pSHGetNameFromIDList(pidl, SIGDN_NORMALDISPLAY, &name);
188                 ok(r == S_OK, "SHGetNameFromIDList failed: %08x\n", r);
189                 todo_wine
190                 ok(!lstrcmpW(title, name), "expected %s, got %s\n",
191                  wine_dbgstr_w(name), wine_dbgstr_w(title));
192                 CoTaskMemFree(name);
193                 CoTaskMemFree(pidl);
194             }
195             else if (pSHGetFolderPathW)
196             {
197                 WCHAR *p;
198 
199                 p = path + lstrlenW(path);
200                 while (path < p && *(p - 1) != '\\')
201                     p--;
202                 ok(!lstrcmpiW(title, p), "expected %s, got %s\n",
203                  wine_dbgstr_w(p), wine_dbgstr_w(title));
204             }
205             else skip("skipping Folder::get_Title test\n");
206             SysFreeString(title);
207         }
208         r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
209         ok(r == S_OK, "Folder::QueryInterface failed: %08x\n", r);
210         if (r == S_OK)
211         {
212             r = Folder2_get_Self(folder2, &item);
213             ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
214             if (r == S_OK)
215             {
216                 r = FolderItem_get_Path(item, &item_path);
217                 ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
218                 if (pSHGetFolderPathW)
219                     ok(!lstrcmpiW(item_path, path), "expected %s, got %s\n",
220                      wine_dbgstr_w(path), wine_dbgstr_w(item_path));
221                 SysFreeString(item_path);
222                 FolderItem_Release(item);
223             }
224             Folder2_Release(folder2);
225         }
226         Folder_Release(folder);
227     }
228 
229     V_VT(&var) = VT_I4;
230     V_I4(&var) = ssfBITBUCKET;
231     r = IShellDispatch_NameSpace(sd, var, &folder);
232     ok(r == S_OK ||
233      broken(r == S_FALSE), /* NT4 */
234      "IShellDispatch::NameSpace failed: %08x\n", r);
235     if (r == S_OK)
236     {
237         r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
238         ok(r == S_OK ||
239          broken(r == E_NOINTERFACE), /* NT4 */
240          "Folder::QueryInterface failed: %08x\n", r);
241         if (r == S_OK)
242         {
243             r = Folder2_get_Self(folder2, &item);
244             ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
245             if (r == S_OK)
246             {
247                 r = FolderItem_get_Path(item, &item_path);
248                 todo_wine
249                 ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
250                 todo_wine
251                 ok(!lstrcmpW(item_path, clsidW), "expected %s, got %s\n",
252                  wine_dbgstr_w(clsidW), wine_dbgstr_w(item_path));
253                 SysFreeString(item_path);
254                 FolderItem_Release(item);
255             }
256             Folder2_Release(folder2);
257         }
258         Folder_Release(folder);
259     }
260 
261     GetTempPathW(MAX_PATH, tempW);
262     GetCurrentDirectoryW(MAX_PATH, curW);
263     SetCurrentDirectoryW(tempW);
264     CreateDirectoryW(winetestW, NULL);
265     V_VT(&var) = VT_BSTR;
266     V_BSTR(&var) = SysAllocString(winetestW);
267     r = IShellDispatch_NameSpace(sd, var, &folder);
268     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
269     SysFreeString(V_BSTR(&var));
270 
271     GetFullPathNameW(winetestW, MAX_PATH, tempW, NULL);
272     if (pGetLongPathNameW)
273     {
274         len = pGetLongPathNameW(tempW, NULL, 0);
275         long_pathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
276         if (long_pathW)
277             pGetLongPathNameW(tempW, long_pathW, len);
278     }
279     V_VT(&var) = VT_BSTR;
280     V_BSTR(&var) = SysAllocString(tempW);
281     r = IShellDispatch_NameSpace(sd, var, &folder);
282     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
283     if (r == S_OK)
284     {
285         r = Folder_get_Title(folder, &title);
286         ok(r == S_OK, "Folder::get_Title failed: %08x\n", r);
287         if (r == S_OK)
288         {
289             ok(!lstrcmpW(title, winetestW), "bad title: %s\n",
290              wine_dbgstr_w(title));
291             SysFreeString(title);
292         }
293         r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
294         ok(r == S_OK ||
295          broken(r == E_NOINTERFACE), /* NT4 */
296          "Folder::QueryInterface failed: %08x\n", r);
297         if (r == S_OK)
298         {
299             r = Folder2_get_Self(folder2, &item);
300             ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
301             if (r == S_OK)
302             {
303                 r = FolderItem_get_Path(item, &item_path);
304                 ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
305                 if (long_pathW)
306                     ok(!lstrcmpW(item_path, long_pathW),
307                      "expected %s, got %s\n", wine_dbgstr_w(long_pathW),
308                      wine_dbgstr_w(item_path));
309                 SysFreeString(item_path);
310                 FolderItem_Release(item);
311             }
312             Folder2_Release(folder2);
313         }
314         Folder_Release(folder);
315     }
316     SysFreeString(V_BSTR(&var));
317 
318     len = lstrlenW(tempW);
319     if (len < MAX_PATH - 1)
320     {
321         lstrcatW(tempW, backslashW);
322         V_VT(&var) = VT_BSTR;
323         V_BSTR(&var) = SysAllocString(tempW);
324         r = IShellDispatch_NameSpace(sd, var, &folder);
325         ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
326         if (r == S_OK)
327         {
328             r = Folder_get_Title(folder, &title);
329             ok(r == S_OK, "Folder::get_Title failed: %08x\n", r);
330             if (r == S_OK)
331             {
332                 ok(!lstrcmpW(title, winetestW), "bad title: %s\n",
333                  wine_dbgstr_w(title));
334                 SysFreeString(title);
335             }
336             r = Folder_QueryInterface(folder, &IID_Folder2, (LPVOID*)&folder2);
337             ok(r == S_OK ||
338              broken(r == E_NOINTERFACE), /* NT4 */
339              "Folder::QueryInterface failed: %08x\n", r);
340             if (r == S_OK)
341             {
342                 r = Folder2_get_Self(folder2, &item);
343                 ok(r == S_OK, "Folder::get_Self failed: %08x\n", r);
344                 if (r == S_OK)
345                 {
346                     r = FolderItem_get_Path(item, &item_path);
347                     ok(r == S_OK, "FolderItem::get_Path failed: %08x\n", r);
348                     if (long_pathW)
349                         ok(!lstrcmpW(item_path, long_pathW),
350                          "expected %s, got %s\n", wine_dbgstr_w(long_pathW),
351                          wine_dbgstr_w(item_path));
352                     SysFreeString(item_path);
353                     FolderItem_Release(item);
354                 }
355                 Folder2_Release(folder2);
356             }
357             Folder_Release(folder);
358         }
359         SysFreeString(V_BSTR(&var));
360     }
361 
362     HeapFree(GetProcessHeap(), 0, long_pathW);
363     RemoveDirectoryW(winetestW);
364     SetCurrentDirectoryW(curW);
365     IShellDispatch_Release(sd);
366 }
367 
368 static void test_items(void)
369 {
370     WCHAR wstr[MAX_PATH], orig_dir[MAX_PATH];
371     HRESULT r;
372     IShellDispatch *sd = NULL;
373     Folder *folder = NULL;
374     FolderItems *items = NULL;
375     FolderItems2 *items2 = NULL;
376     FolderItems3 *items3 = NULL;
377     FolderItem *item = (FolderItem*)0xdeadbeef;
378     IDispatch *disp = NULL;
379     IUnknown *unk = NULL;
380     FolderItemVerbs *verbs = (FolderItemVerbs*)0xdeadbeef;
381     VARIANT var;
382     LONG lcount = -1;
383 
384     r = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, &IID_IShellDispatch, (void**)&sd);
385     ok(SUCCEEDED(r), "CoCreateInstance failed: %08x\n", r);
386     ok(!!sd, "sd is null\n");
387 
388     GetTempPathW(MAX_PATH, wstr);
389     GetCurrentDirectoryW(MAX_PATH, orig_dir);
390     SetCurrentDirectoryW(wstr);
391     CreateDirectoryW(winetestW, NULL);
392     GetFullPathNameW(winetestW, MAX_PATH, wstr, NULL);
393     V_VT(&var) = VT_BSTR;
394     V_BSTR(&var) = SysAllocString(wstr);
395     r = IShellDispatch_NameSpace(sd, var, &folder);
396     ok(r == S_OK, "IShellDispatch::NameSpace failed: %08x\n", r);
397     ok(!!folder, "folder is null\n");
398     SysFreeString(V_BSTR(&var));
399     IShellDispatch_Release(sd);
400     SetCurrentDirectoryW(winetestW);
401 
402     r = Folder_Items(folder, &items);
403     ok(r == S_OK, "Folder::Items failed: %08x\n", r);
404     ok(!!items, "items is null\n");
405     r = FolderItems_QueryInterface(items, &IID_FolderItems2, (void**)&items2);
406     ok(r == S_OK || broken(r == E_NOINTERFACE) /* xp and later */, "FolderItems::QueryInterface failed: %08x\n", r);
407     ok(!!items2 || broken(!items2) /* xp and later */, "items2 is null\n");
408     r = FolderItems_QueryInterface(items, &IID_FolderItems3, (void**)&items3);
409     ok(r == S_OK, "FolderItems::QueryInterface failed: %08x\n", r);
410     ok(!!items3, "items3 is null\n");
411     Folder_Release(folder);
412 
413     if (0) /* crashes on all versions of Windows */
414         r = FolderItems_get_Count(items, NULL);
415 
416     r = FolderItems_get_Count(items, &lcount);
417     ok(r == S_OK, "FolderItems::get_Count failed: %08x\n", r);
418     ok(!lcount, "expected 0 files, got %d\n", lcount);
419 
420     V_VT(&var) = VT_I4;
421     V_I4(&var) = 0;
422 
423     if (0) /* crashes on all versions of Windows */
424         r = FolderItems_Item(items, var, NULL);
425 
426     r = FolderItems_Item(items, var, &item);
427 todo_wine
428     ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
429     ok(!item, "item is not null\n");
430 
431     if (0) /* crashes on xp */
432     {
433         r = FolderItems_get_Application(items, NULL);
434         ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
435     }
436 
437     r = FolderItems_get_Application(items, &disp);
438 todo_wine
439     ok(r == S_OK, "FolderItems::get_Application failed: %08x\n", r);
440 todo_wine
441     ok(!!disp, "disp is null\n");
442     if (disp) IDispatch_Release(disp);
443 
444     if (0) /* crashes on xp */
445     {
446         r = FolderItems_get_Parent(items, NULL);
447         ok(r == E_NOTIMPL, "expected E_NOTIMPL, got %08x\n", r);
448     }
449 
450     disp = (IDispatch*)0xdeadbeef;
451     r = FolderItems_get_Parent(items, &disp);
452     ok(r == E_NOTIMPL, "expected E_NOTIMPL, got %08x\n", r);
453     ok(!disp, "disp is not null\n");
454 
455     if (0) /* crashes on xp */
456     {
457         r = FolderItems__NewEnum(items, NULL);
458         ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
459     }
460 
461     r = FolderItems__NewEnum(items, &unk);
462 todo_wine
463     ok(r == S_OK, "FolderItems::_NewEnum failed: %08x\n", r);
464 todo_wine
465     ok(!!unk, "unk is null\n");
466     if (unk) IUnknown_Release(unk);
467 
468     if (items3)
469     {
470         r = FolderItems3_Filter(items3, 0, NULL);
471 todo_wine
472         ok(r == S_OK, "expected S_OK, got %08x\n", r);
473 
474         if (0) /* crashes on xp */
475         {
476             r = FolderItems3_get_Verbs(items3, NULL);
477             ok(r == E_INVALIDARG, "expected E_INVALIDARG, got %08x\n", r);
478         }
479 
480         r = FolderItems3_get_Verbs(items3, &verbs);
481 todo_wine
482         ok(r == S_FALSE, "expected S_FALSE, got %08x\n", r);
483         ok(!verbs, "verbs is not null\n");
484     }
485 
486     GetTempPathW(MAX_PATH, wstr);
487     SetCurrentDirectoryW(wstr);
488     RemoveDirectoryW(winetestW);
489     SetCurrentDirectoryW(orig_dir);
490 
491     FolderItems_Release(items);
492     if (items2) FolderItems2_Release(items2);
493     if (items3) FolderItems3_Release(items3);
494 }
495 
496 static void test_service(void)
497 {
498     static const WCHAR spooler[] = {'S','p','o','o','l','e','r',0};
499     static const WCHAR dummyW[] = {'d','u','m','m','y',0};
500     SERVICE_STATUS_PROCESS status;
501     SC_HANDLE scm, service;
502     IShellDispatch2 *sd;
503     DWORD dummy;
504     HRESULT hr;
505     BSTR name;
506     VARIANT v;
507 
508     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
509         &IID_IShellDispatch2, (void**)&sd);
510     if (hr != S_OK)
511     {
512         win_skip("IShellDispatch2 not supported\n");
513         return;
514     }
515 
516     V_VT(&v) = VT_I2;
517     V_I2(&v) = 10;
518     hr = IShellDispatch2_IsServiceRunning(sd, NULL, &v);
519     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
520     ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
521     EXPECT_HR(hr, S_OK);
522 
523     scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
524     service = OpenServiceW(scm, spooler, SERVICE_QUERY_STATUS);
525     QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (BYTE *)&status, sizeof(SERVICE_STATUS_PROCESS), &dummy);
526     CloseServiceHandle(service);
527     CloseServiceHandle(scm);
528 
529     /* service should exist */
530     name = SysAllocString(spooler);
531     V_VT(&v) = VT_I2;
532     hr = IShellDispatch2_IsServiceRunning(sd, name, &v);
533     EXPECT_HR(hr, S_OK);
534     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
535     if (status.dwCurrentState == SERVICE_RUNNING)
536         ok(V_BOOL(&v) == VARIANT_TRUE, "got %d\n", V_BOOL(&v));
537     else
538         ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
539     SysFreeString(name);
540 
541     /* service doesn't exist */
542     name = SysAllocString(dummyW);
543     V_VT(&v) = VT_I2;
544     hr = IShellDispatch2_IsServiceRunning(sd, name, &v);
545     EXPECT_HR(hr, S_OK);
546     ok(V_VT(&v) == VT_BOOL, "got %d\n", V_VT(&v));
547     ok(V_BOOL(&v) == VARIANT_FALSE, "got %d\n", V_BOOL(&v));
548     SysFreeString(name);
549 
550     IShellDispatch2_Release(sd);
551 }
552 
553 static void test_dispatch_typeinfo(IDispatch *disp, REFIID *riid)
554 {
555     ITypeInfo *typeinfo;
556     TYPEATTR *typeattr;
557     UINT count;
558     HRESULT hr;
559 
560     count = 10;
561     hr = IDispatch_GetTypeInfoCount(disp, &count);
562     ok(hr == S_OK, "got 0x%08x\n", hr);
563     ok(count == 1, "got %u\n", count);
564 
565     hr = IDispatch_GetTypeInfo(disp, 0, LOCALE_SYSTEM_DEFAULT, &typeinfo);
566     ok(hr == S_OK, "got 0x%08x\n", hr);
567 
568     hr = ITypeInfo_GetTypeAttr(typeinfo, &typeattr);
569     ok(hr == S_OK, "got 0x%08x\n", hr);
570     while (!IsEqualGUID(*riid, &IID_NULL)) {
571         if (IsEqualGUID(&typeattr->guid, *riid))
572             break;
573         riid++;
574     }
575     ok(IsEqualGUID(&typeattr->guid, *riid), "unexpected type guid %s\n", wine_dbgstr_guid(&typeattr->guid));
576 
577     ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr);
578     ITypeInfo_Release(typeinfo);
579 }
580 
581 static void test_ShellFolderViewDual(void)
582 {
583     static const IID *shelldisp_riids[] = {
584         &IID_IShellDispatch6,
585         &IID_IShellDispatch5,
586         &IID_IShellDispatch4,
587         &IID_IShellDispatch2,
588         &IID_IWin7ShellDispatch6,
589         &IID_NULL
590     };
591     IShellFolderViewDual *viewdual;
592     IShellFolder *desktop, *tmpdir;
593     IShellView *view, *view2;
594     IDispatch *disp, *disp2;
595     WCHAR pathW[MAX_PATH];
596     LPITEMIDLIST pidl;
597     HRESULT hr;
598 
599     /* IShellFolderViewDual is not an IShellView extension */
600     hr = SHGetDesktopFolder(&desktop);
601     ok(hr == S_OK, "got 0x%08x\n", hr);
602 
603     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
604     ok(hr == S_OK, "got 0x%08x\n", hr);
605 
606     hr = IShellView_QueryInterface(view, &IID_IShellFolderViewDual, (void**)&viewdual);
607     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
608 
609     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp);
610     ok(hr == S_OK, "got 0x%08x\n", hr);
611 
612     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp2);
613     ok(hr == S_OK, "got 0x%08x\n", hr);
614     ok(disp2 == disp, "got %p, %p\n", disp2, disp);
615     IDispatch_Release(disp2);
616 
617     hr = IDispatch_QueryInterface(disp, &IID_IShellFolderViewDual, (void**)&viewdual);
618     ok(hr == S_OK, "got 0x%08x\n", hr);
619     ok(disp == (IDispatch*)viewdual, "got %p, expected %p\n", viewdual, disp);
620 
621     hr = IShellFolderViewDual_QueryInterface(viewdual, &IID_IShellView, (void**)&view2);
622     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
623 
624     /* get_Application() */
625 
626 if (0) /* crashes on pre-vista */ {
627     hr = IShellFolderViewDual_get_Application(viewdual, NULL);
628     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
629 }
630     hr = IShellFolderViewDual_get_Application(viewdual, &disp2);
631     ok(hr == S_OK, "got 0x%08x\n", hr);
632     ok(disp2 != (IDispatch*)viewdual, "got %p, %p\n", disp2, viewdual);
633     test_dispatch_typeinfo(disp2, shelldisp_riids);
634     IDispatch_Release(disp2);
635 
636     IShellFolderViewDual_Release(viewdual);
637     IDispatch_Release(disp);
638 
639     disp = (void*)0xdeadbeef;
640     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IShellFolderViewDual, (void**)&disp);
641     ok(hr == E_NOINTERFACE || broken(hr == E_NOTIMPL) /* win2k */, "got 0x%08x\n", hr);
642     ok(disp == NULL, "got %p\n", disp);
643     IShellView_Release(view);
644 
645     /* Try with some other folder, that's not a desktop */
646     GetTempPathW(sizeof(pathW)/sizeof(pathW[0]), pathW);
647     hr = IShellFolder_ParseDisplayName(desktop, NULL, NULL, pathW, NULL, &pidl, NULL);
648     ok(hr == S_OK, "got 0x%08x\n", hr);
649 
650     hr = IShellFolder_BindToObject(desktop, pidl, NULL, &IID_IShellFolder, (void**)&tmpdir);
651     ok(hr == S_OK, "got 0x%08x\n", hr);
652     CoTaskMemFree(pidl);
653 
654     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
655     ok(hr == S_OK, "got 0x%08x\n", hr);
656 
657     hr = IShellView_QueryInterface(view, &IID_IShellFolderViewDual, (void**)&viewdual);
658     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
659 
660     hr = IShellView_GetItemObject(view, SVGIO_BACKGROUND, &IID_IDispatch, (void**)&disp);
661     ok(hr == S_OK, "got 0x%08x\n", hr);
662     IDispatch_Release(disp);
663     IShellView_Release(view);
664 
665     IShellFolder_Release(tmpdir);
666     IShellFolder_Release(desktop);
667 }
668 
669 static void test_ShellWindows(void)
670 {
671     IShellWindows *shellwindows;
672     LONG cookie, cookie2, ret;
673     IDispatch *disp;
674     VARIANT v, v2;
675     HRESULT hr;
676     HWND hwnd;
677 
678     hr = CoCreateInstance(&CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER,
679         &IID_IShellWindows, (void**)&shellwindows);
680     ok(hr == S_OK, "got 0x%08x\n", hr);
681     /* TODO: remove when explorer startup with clean prefix is fixed */
682     if (hr != S_OK)
683         return;
684 
685 if (0) /* NULL out argument - currently crashes on Wine */ {
686     hr = IShellWindows_Register(shellwindows, NULL, 0, SWC_EXPLORER, NULL);
687     ok(hr == HRESULT_FROM_WIN32(RPC_X_NULL_REF_POINTER), "got 0x%08x\n", hr);
688 }
689     hr = IShellWindows_Register(shellwindows, NULL, 0, SWC_EXPLORER, &cookie);
690 todo_wine
691     ok(hr == E_POINTER, "got 0x%08x\n", hr);
692 
693     hr = IShellWindows_Register(shellwindows, (IDispatch*)shellwindows, 0, SWC_EXPLORER, &cookie);
694 todo_wine
695     ok(hr == E_POINTER, "got 0x%08x\n", hr);
696 
697     hr = IShellWindows_Register(shellwindows, (IDispatch*)shellwindows, 0, SWC_EXPLORER, &cookie);
698 todo_wine
699     ok(hr == E_POINTER, "got 0x%08x\n", hr);
700 
701     hwnd = CreateWindowExA(0, "button", "test", BS_CHECKBOX | WS_VISIBLE | WS_POPUP,
702                            0, 0, 50, 14, 0, 0, 0, NULL);
703     ok(hwnd != NULL, "got %p, error %d\n", hwnd, GetLastError());
704 
705     cookie = 0;
706     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_EXPLORER, &cookie);
707 todo_wine {
708     ok(hr == S_OK, "got 0x%08x\n", hr);
709     ok(cookie != 0, "got %d\n", cookie);
710 }
711     cookie2 = 0;
712     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_EXPLORER, &cookie2);
713 todo_wine {
714     ok(hr == S_OK, "got 0x%08x\n", hr);
715     ok(cookie2 != 0 && cookie2 != cookie, "got %d\n", cookie2);
716 }
717     hr = IShellWindows_Revoke(shellwindows, cookie);
718 todo_wine
719     ok(hr == S_OK, "got 0x%08x\n", hr);
720     hr = IShellWindows_Revoke(shellwindows, cookie2);
721 todo_wine
722     ok(hr == S_OK, "got 0x%08x\n", hr);
723 
724     hr = IShellWindows_Revoke(shellwindows, 0);
725 todo_wine
726     ok(hr == S_FALSE, "got 0x%08x\n", hr);
727 
728     /* we can register ourselves as desktop, but FindWindowSW still returns real desktop window */
729     cookie = 0;
730     hr = IShellWindows_Register(shellwindows, NULL, HandleToLong(hwnd), SWC_DESKTOP, &cookie);
731 todo_wine {
732     ok(hr == S_OK, "got 0x%08x\n", hr);
733     ok(cookie != 0, "got %d\n", cookie);
734 }
735     disp = (void*)0xdeadbeef;
736     ret = 0xdead;
737     VariantInit(&v);
738     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v, SWC_DESKTOP, &ret, SWFO_NEEDDISPATCH, &disp);
739     ok(hr == S_OK || broken(hr == S_FALSE), "got 0x%08x\n", hr);
740     if (hr == S_FALSE) /* winxp and earlier */ {
741         win_skip("SWC_DESKTOP is not supported, some tests will be skipped.\n");
742         /* older versions allowed to regiser SWC_DESKTOP and access it with FindWindowSW */
743         ok(disp == NULL, "got %p\n", disp);
744         ok(ret == 0, "got %d\n", ret);
745     }
746     else {
747         static const IID *browser_riids[] = {
748             &IID_IWebBrowser2,
749             &IID_NULL
750         };
751 
752         static const IID *viewdual_riids[] = {
753             &IID_IShellFolderViewDual3,
754             &IID_NULL
755         };
756 
757         IShellFolderViewDual *view;
758         IShellBrowser *sb, *sb2;
759         IServiceProvider *sp;
760         IDispatch *doc, *app;
761         IWebBrowser2 *wb;
762         IShellView *sv;
763         IUnknown *unk;
764 
765         ok(disp != NULL, "got %p\n", disp);
766         ok(ret != HandleToUlong(hwnd), "got %d\n", ret);
767 
768         /* IDispatch-related tests */
769         test_dispatch_typeinfo(disp, browser_riids);
770 
771         /* IWebBrowser2 */
772         hr = IDispatch_QueryInterface(disp, &IID_IWebBrowser2, (void**)&wb);
773         ok(hr == S_OK, "got 0x%08x\n", hr);
774 
775         hr = IWebBrowser2_Refresh(wb);
776 todo_wine
777         ok(hr == S_OK, "got 0x%08x\n", hr);
778 
779         hr = IWebBrowser2_get_Application(wb, &app);
780         ok(hr == S_OK, "got 0x%08x\n", hr);
781         ok(disp == app, "got %p, %p\n", app, disp);
782         IDispatch_Release(app);
783 
784         hr = IWebBrowser2_get_Document(wb, &doc);
785 todo_wine
786         ok(hr == S_OK, "got 0x%08x\n", hr);
787 if (hr == S_OK) {
788         test_dispatch_typeinfo(doc, viewdual_riids);
789 }
790         IWebBrowser2_Release(wb);
791 
792         /* IServiceProvider */
793         hr = IDispatch_QueryInterface(disp, &IID_IShellFolderViewDual, (void**)&view);
794         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
795 
796         hr = IDispatch_QueryInterface(disp, &IID_IServiceProvider, (void**)&sp);
797         ok(hr == S_OK, "got 0x%08x\n", hr);
798 
799         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellBrowser, (void**)&sb);
800         ok(hr == S_OK, "got 0x%08x\n", hr);
801 
802         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellBrowser, (void**)&sb2);
803         ok(hr == S_OK, "got 0x%08x\n", hr);
804         ok(sb == sb2, "got %p, %p\n", sb, sb2);
805 
806         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IOleWindow, (void**)&unk);
807         ok(hr == S_OK, "got 0x%08x\n", hr);
808         IUnknown_Release(unk);
809 
810         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IExplorerBrowser, (void**)&unk);
811         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
812 
813         hr = IShellBrowser_QueryInterface(sb, &IID_IExplorerBrowser, (void**)&unk);
814         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
815 
816         hr = IShellBrowser_QueryInterface(sb, &IID_IWebBrowser2, (void**)&unk);
817         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
818 
819         hr = IShellBrowser_QueryInterface(sb, &IID_IDispatch, (void**)&unk);
820         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
821 
822         hr = IShellBrowser_QueryActiveShellView(sb, &sv);
823         ok(hr == S_OK, "got 0x%08x\n", hr);
824         IShellView_Release(sv);
825 
826         IShellBrowser_Release(sb2);
827         IShellBrowser_Release(sb);
828 
829         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IUnknown, (void**)&unk);
830         ok(hr == S_OK, "got 0x%08x\n", hr);
831 
832         hr = IUnknown_QueryInterface(unk, &IID_IShellBrowser, (void**)&sb2);
833         ok(hr == S_OK, "got 0x%08x\n", hr);
834         IShellBrowser_Release(sb2);
835         IUnknown_Release(unk);
836 
837         hr = IServiceProvider_QueryService(sp, &SID_STopLevelBrowser, &IID_IShellView, (void**)&sv);
838         ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
839 
840         IServiceProvider_Release(sp);
841         IDispatch_Release(disp);
842     }
843 
844     disp = (void*)0xdeadbeef;
845     ret = 0xdead;
846     VariantInit(&v);
847     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v, SWC_DESKTOP, &ret, 0, &disp);
848     ok(hr == S_OK || broken(hr == S_FALSE) /* winxp */, "got 0x%08x\n", hr);
849     ok(disp == NULL, "got %p\n", disp);
850     ok(ret != HandleToUlong(hwnd), "got %d\n", ret);
851 
852     disp = (void*)0xdeadbeef;
853     ret = 0xdead;
854     V_VT(&v) = VT_I4;
855     V_I4(&v) = cookie;
856     VariantInit(&v2);
857     hr = IShellWindows_FindWindowSW(shellwindows, &v, &v2, SWC_BROWSER, &ret, SWFO_COOKIEPASSED, &disp);
858 todo_wine
859     ok(hr == S_FALSE, "got 0x%08x\n", hr);
860     ok(disp == NULL, "got %p\n", disp);
861     ok(ret == 0, "got %d\n", ret);
862 
863     hr = IShellWindows_Revoke(shellwindows, cookie);
864 todo_wine
865     ok(hr == S_OK, "got 0x%08x\n", hr);
866     DestroyWindow(hwnd);
867     IShellWindows_Release(shellwindows);
868 }
869 
870 static void test_ParseName(void)
871 {
872     static const WCHAR cadabraW[] = {'c','a','d','a','b','r','a',0};
873     WCHAR pathW[MAX_PATH];
874     IShellDispatch *sd;
875     FolderItem *item;
876     Folder *folder;
877     HRESULT hr;
878     VARIANT v;
879     BSTR str;
880 
881     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
882         &IID_IShellDispatch, (void**)&sd);
883     ok(hr == S_OK, "got 0x%08x\n", hr);
884 
885     GetTempPathW(sizeof(pathW)/sizeof(pathW[0]), pathW);
886     V_VT(&v) = VT_BSTR;
887     V_BSTR(&v) = SysAllocString(pathW);
888     hr = IShellDispatch_NameSpace(sd, v, &folder);
889     ok(hr == S_OK, "got 0x%08x\n", hr);
890     VariantClear(&v);
891 
892     item = (void*)0xdeadbeef;
893     hr = Folder_ParseName(folder, NULL, &item);
894     ok(hr == S_FALSE || broken(hr == E_INVALIDARG) /* win2k */, "got 0x%08x\n", hr);
895     ok(item == NULL, "got %p\n", item);
896 
897     /* empty name */
898     str = SysAllocStringLen(NULL, 0);
899     item = (void*)0xdeadbeef;
900     hr = Folder_ParseName(folder, str, &item);
901     ok(hr == S_FALSE || broken(hr == E_INVALIDARG) /* win2k */, "got 0x%08x\n", hr);
902     ok(item == NULL, "got %p\n", item);
903     SysFreeString(str);
904 
905     /* path doesn't exist */
906     str = SysAllocString(cadabraW);
907     item = (void*)0xdeadbeef;
908     hr = Folder_ParseName(folder, str, &item);
909     ok(hr == S_FALSE || broken(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) /* win2k */,
910         "got 0x%08x\n", hr);
911     ok(item == NULL, "got %p\n", item);
912     SysFreeString(str);
913 
914     lstrcatW(pathW, cadabraW);
915     CreateDirectoryW(pathW, NULL);
916 
917     str = SysAllocString(cadabraW);
918     item = NULL;
919     hr = Folder_ParseName(folder, str, &item);
920     ok(hr == S_OK, "got 0x%08x\n", hr);
921     ok(item != NULL, "got %p\n", item);
922     SysFreeString(str);
923 
924     hr = FolderItem_get_Path(item, &str);
925     ok(hr == S_OK, "got 0x%08x\n", hr);
926     ok(str[0] != 0, "path %s\n", wine_dbgstr_w(str));
927     SysFreeString(str);
928 
929     RemoveDirectoryW(pathW);
930     FolderItem_Release(item);
931     Folder_Release(folder);
932     IShellDispatch_Release(sd);
933 }
934 
935 static void test_Verbs(void)
936 {
937     FolderItemVerbs *verbs;
938     WCHAR pathW[MAX_PATH];
939     FolderItemVerb *verb;
940     IShellDispatch *sd;
941     FolderItem *item;
942     Folder2 *folder2;
943     Folder *folder;
944     HRESULT hr;
945     LONG count, i;
946     VARIANT v;
947     BSTR str;
948 
949     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
950         &IID_IShellDispatch, (void**)&sd);
951     ok(hr == S_OK, "got 0x%08x\n", hr);
952 
953     GetTempPathW(sizeof(pathW)/sizeof(pathW[0]), pathW);
954     V_VT(&v) = VT_BSTR;
955     V_BSTR(&v) = SysAllocString(pathW);
956     hr = IShellDispatch_NameSpace(sd, v, &folder);
957     ok(hr == S_OK, "got 0x%08x\n", hr);
958     VariantClear(&v);
959 
960     hr = Folder_QueryInterface(folder, &IID_Folder2, (void**)&folder2);
961     ok(hr == S_OK, "got 0x%08x\n", hr);
962     Folder_Release(folder);
963 
964     hr = Folder2_get_Self(folder2, &item);
965     ok(hr == S_OK, "got 0x%08x\n", hr);
966     Folder2_Release(folder2);
967 
968 if (0) { /* crashes on some systems */
969     hr = FolderItem_Verbs(item, NULL);
970     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
971 }
972     hr = FolderItem_Verbs(item, &verbs);
973     ok(hr == S_OK, "got 0x%08x\n", hr);
974 
975 if (0) { /* crashes on winxp/win2k3 */
976     hr = FolderItemVerbs_get_Count(verbs, NULL);
977     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
978 }
979     count = 0;
980     hr = FolderItemVerbs_get_Count(verbs, &count);
981     ok(hr == S_OK, "got 0x%08x\n", hr);
982     ok(count > 0, "got count %d\n", count);
983 
984 if (0) { /* crashes on winxp/win2k3 */
985     V_VT(&v) = VT_I4;
986     V_I4(&v) = 0;
987     hr = FolderItemVerbs_Item(verbs, v, NULL);
988     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
989 }
990     /* there's always one item more, so you can access [0,count],
991        instead of actual [0,count) */
992     for (i = 0; i <= count; i++) {
993         V_VT(&v) = VT_I4;
994         V_I4(&v) = i;
995         hr = FolderItemVerbs_Item(verbs, v, &verb);
996         ok(hr == S_OK, "got 0x%08x\n", hr);
997         hr = FolderItemVerb_get_Name(verb, &str);
998         ok(hr == S_OK, "got 0x%08x\n", hr);
999         ok(str != NULL, "%d: name %s\n", i, wine_dbgstr_w(str));
1000         if (i == count)
1001             ok(str[0] == 0, "%d: got teminating item %s\n", i, wine_dbgstr_w(str));
1002 
1003         SysFreeString(str);
1004         FolderItemVerb_Release(verb);
1005     }
1006 
1007     V_VT(&v) = VT_I4;
1008     V_I4(&v) = count+1;
1009     verb = NULL;
1010     hr = FolderItemVerbs_Item(verbs, v, &verb);
1011     ok(hr == S_OK, "got 0x%08x\n", hr);
1012     ok(verb == NULL, "got %p\n", verb);
1013 
1014     FolderItemVerbs_Release(verbs);
1015     FolderItem_Release(item);
1016     IShellDispatch_Release(sd);
1017 }
1018 
1019 static void test_ShellExecute(void)
1020 {
1021     HRESULT hr;
1022     IShellDispatch2 *sd;
1023     BSTR name;
1024     VARIANT args, dir, op, show;
1025 
1026     static const WCHAR regW[] = {'r','e','g',0};
1027 
1028     hr = CoCreateInstance(&CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
1029         &IID_IShellDispatch2, (void**)&sd);
1030     if (hr != S_OK)
1031     {
1032         win_skip("IShellDispatch2 not supported\n");
1033         return;
1034     }
1035 
1036     VariantInit(&args);
1037     VariantInit(&dir);
1038     VariantInit(&op);
1039     VariantInit(&show);
1040 
1041     V_VT(&show) = VT_I4;
1042     V_I4(&show) = 0;
1043 
1044     name = SysAllocString(regW);
1045 
1046     hr = IShellDispatch2_ShellExecute(sd, name, args, dir, op, show);
1047     ok(hr == S_OK, "ShellExecute failed: %08x\n", hr);
1048 
1049     /* test invalid value for show */
1050     V_VT(&show) = VT_BSTR;
1051     V_BSTR(&show) = name;
1052 
1053     hr = IShellDispatch2_ShellExecute(sd, name, args, dir, op, show);
1054     ok(hr == S_OK, "ShellExecute failed: %08x\n", hr);
1055 
1056     SysFreeString(name);
1057 }
1058 
1059 START_TEST(shelldispatch)
1060 {
1061     HRESULT r;
1062 
1063     r = CoInitialize(NULL);
1064     ok(SUCCEEDED(r), "CoInitialize failed: %08x\n", r);
1065     if (FAILED(r))
1066         return;
1067 
1068     init_function_pointers();
1069     test_namespace();
1070     test_items();
1071     test_service();
1072     test_ShellFolderViewDual();
1073     test_ShellWindows();
1074     test_ParseName();
1075     test_Verbs();
1076     test_ShellExecute();
1077 
1078     CoUninitialize();
1079 }
1080