1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for ACListISF objects
5  * PROGRAMMER:      Mark Jansen
6  */
7 
8 #define _UNICODE
9 #define UNICODE
10 #include <apitest.h>
11 #include <shlobj.h>
12 #include <atlbase.h>
13 #include <tchar.h>      //
14 #include <atlcom.h>     // These 3 includes only exist here to make gcc happy about (unused) templates..
15 #include <atlwin.h>     //
16 
17 // Yes, gcc at it again, let's validate everything found inside unused templates!
18 ULONG DbgPrint(PCH Format,...);
19 
20 #include <stdio.h>
21 #include <shellutils.h>
22 #include <shlwapi.h>
23 #include <strsafe.h>
24 
25 static bool g_ShowHidden;
26 static DWORD g_WinVersion;
27 #define WINVER_VISTA   0x0600
28 
29 
30 #define ok_hr(status, expected)     ok_hex(status, expected)
31 
32 // We do not want our results to originate from the helper functions, so have them originate from the calls to them
33 #define test_at_end                 (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_at_end_imp
34 #define test_ExpectDrives           (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_ExpectDrives_imp
35 #define test_ExpectFolders          (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : test_ExpectFolders_imp
36 #define winetest_ok_hr(expression, expected) \
37     do { \
38         int _value = (expression); \
39         winetest_ok(_value == (expected), "Wrong value for '%s', expected: " #expected " (0x%x), got: 0x%x\n", \
40            #expression, (int)(expected), _value); \
41     } while (0)
42 
43 
44 
45 
46 static void test_at_end_imp(CComPtr<IEnumString>& EnumStr)
47 {
48     CComHeapPtr<OLECHAR> Result;
49     ULONG Fetched = 12345;
50     HRESULT hr = EnumStr->Next(1, &Result, &Fetched);
51     winetest_ok(hr == S_FALSE, "Expected hr to be S_FALSE, was 0x%lx\n", hr);
52     winetest_ok(Fetched == 0u, "Expected Fetched to be 0, was: %lu\n", Fetched);
53     if (Fetched == 1u)
54         winetest_ok(0, "Expected there not to be a result, got: %s\n", wine_dbgstr_w(Result));
55 }
56 
57 static bool GetDisplayname(CComPtr<IShellFolder>& spDrives, CComHeapPtr<ITEMIDLIST>& pidl, CComHeapPtr<WCHAR>& DisplayName)
58 {
59     STRRET StrRet;
60     HRESULT hr;
61     winetest_ok_hr(hr = spDrives->GetDisplayNameOf(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, &StrRet), S_OK);
62     if (!SUCCEEDED(hr))
63         return false;
64 
65     winetest_ok_hr(hr = StrRetToStrW(&StrRet, NULL, &DisplayName), S_OK);
66     if (!SUCCEEDED(hr))
67         return false;
68     return true;
69 }
70 
71 enum ExpectOptions
72 {
73     None = 0,
74     IgnoreRoot = 1,
75     CheckLast = 2,
76     IgnoreHidden = 4,
77     IgnoreFiles = 8,
78 };
79 
80 // wtf c++
81 ExpectOptions operator | (const ExpectOptions& left, const ExpectOptions& right)
82 {
83     return static_cast<ExpectOptions>(static_cast<int>(left) | static_cast<int>(right));
84 }
85 
86 
87 static void
88 test_ExpectFolders_imp(CComPtr<IEnumString>& EnumStr, LPITEMIDLIST pidlTarget, const WCHAR* Root, ExpectOptions options)
89 {
90     CComPtr<IShellFolder> spDesktop;
91     HRESULT hr = SHGetDesktopFolder(&spDesktop);
92 
93     CComPtr<IShellFolder> spTarget;
94     if (pidlTarget)
95     {
96         winetest_ok_hr(hr = spDesktop->BindToObject(pidlTarget, NULL, IID_PPV_ARG(IShellFolder, &spTarget)), S_OK);
97         if (!SUCCEEDED(hr))
98             return;
99     }
100     else
101     {
102         spTarget = spDesktop;
103     }
104 
105     SHCONTF EnumFlags = SHCONTF_FOLDERS | SHCONTF_INIT_ON_FIRST_NEXT;
106     if (g_ShowHidden && !(options & IgnoreHidden))
107         EnumFlags |= SHCONTF_INCLUDEHIDDEN;
108     if (!(options & IgnoreFiles))
109         EnumFlags |= SHCONTF_NONFOLDERS;
110 
111     CComPtr<IEnumIDList> spEnumIDList;
112     winetest_ok_hr(hr = spTarget->EnumObjects(NULL, EnumFlags, &spEnumIDList), S_OK);
113     if (!SUCCEEDED(hr))
114         return;
115 
116     WCHAR Buffer[512];
117     CComHeapPtr<ITEMIDLIST> pidl;
118     INT Count = 0;
119     while (spEnumIDList->Next(1, &pidl, NULL) == S_OK)
120     {
121         CComHeapPtr<WCHAR> DisplayName;
122         if (!GetDisplayname(spTarget, pidl, DisplayName))
123             break;
124 
125         CComHeapPtr<OLECHAR> Result;
126         ULONG Fetched;
127         hr = EnumStr->Next(1, &Result, &Fetched);
128         winetest_ok_hr(hr, S_OK);
129 
130 
131         if (hr != S_OK)
132             break;
133 
134 
135         StringCchPrintfW(Buffer, _ARRAYSIZE(Buffer), L"%s%s", (options & IgnoreRoot) ? L"" : Root, (WCHAR*)DisplayName);
136 
137         winetest_ok(!wcscmp(Buffer, Result), "Expected %s, got %s\n", wine_dbgstr_w(Buffer), wine_dbgstr_w(Result));
138 
139         pidl.Free();
140         Count++;
141     }
142     if (options & CheckLast)
143     {
144         test_at_end_imp(EnumStr);
145     }
146 }
147 
148 static void
149 test_ExpectDrives_imp(CComPtr<IEnumString>& EnumStr, CComHeapPtr<ITEMIDLIST>& pidlTarget)
150 {
151     test_ExpectFolders_imp(EnumStr, pidlTarget, NULL, IgnoreRoot | CheckLast);
152 }
153 
154 static void
155 test_ACListISF_NONE()
156 {
157     CComPtr<IEnumString> EnumStr;
158     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
159     ok_hr(hr, S_OK);
160     if (!SUCCEEDED(hr))
161         return;
162 
163     CComPtr<IACList2> ACList;
164     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
165     if (!SUCCEEDED(hr))
166         return;
167 
168     ok_hr(hr = ACList->SetOptions(ACLO_NONE), S_OK);
169     test_at_end(EnumStr);
170 
171 
172     WCHAR Buffer[MAX_PATH];
173     GetSystemWindowsDirectoryW(Buffer, _ARRAYSIZE(Buffer));
174     Buffer[3] = '\0';
175 
176     CComHeapPtr<ITEMIDLIST> pidlDiskRoot;
177     ok_hr(hr = SHParseDisplayName(Buffer, NULL, &pidlDiskRoot, NULL, NULL), S_OK);
178     if (!SUCCEEDED(hr))
179         return;
180 
181     ok_hr(hr = ACList->Expand(Buffer), S_OK);
182     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, CheckLast | IgnoreHidden);
183 
184     ok_hr(hr = EnumStr->Reset(), S_OK);
185     ok_hr(hr = ACList->Expand(Buffer), S_OK);
186     ok_hr(hr = ACList->SetOptions(ACLO_NONE), S_OK);
187     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, CheckLast);
188 }
189 
190 static void
191 test_ACListISF_CURRENTDIR()
192 {
193     CComPtr<IEnumString> EnumStr;
194     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
195     ok_hr(hr, S_OK);
196     if (!SUCCEEDED(hr))
197         return;
198 
199     CComPtr<IACList2> ACList;
200     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
201     if (!SUCCEEDED(hr))
202         return;
203 
204     CComPtr<ICurrentWorkingDirectory> CurrentWorkingDir;
205     ok_hr(hr = EnumStr->QueryInterface(IID_ICurrentWorkingDirectory, (void**)&CurrentWorkingDir), S_OK);
206     if (!SUCCEEDED(hr))
207         return;
208 
209     ok_hr(hr = ACList->SetOptions(ACLO_CURRENTDIR), S_OK);
210     test_at_end(EnumStr);
211 
212 
213     WCHAR Buffer[MAX_PATH] = { 1, 1, 1, 1, 1, 0 }, Buffer2[MAX_PATH];
214     if (g_WinVersion < WINVER_VISTA)
215         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), E_NOTIMPL);
216     else
217         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), E_UNEXPECTED);
218     ok(!wcscmp(L"\x1\x1\x1\x1\x1", Buffer), "Expected %s, got %s\n", wine_dbgstr_w(L"\x1\x1\x1\x1\x1"), wine_dbgstr_w(Buffer));
219 
220     GetSystemWindowsDirectoryW(Buffer2, _ARRAYSIZE(Buffer2));
221     // Windows 2k3 does not parse it without the trailing '\\'
222     Buffer2[3] = '\0';
223     CComHeapPtr<ITEMIDLIST> pidlDiskRoot;
224     ok_hr(hr = SHParseDisplayName(Buffer2, NULL, &pidlDiskRoot, NULL, NULL), S_OK);
225     if (!SUCCEEDED(hr))
226         return;
227 
228     ok_hr(hr = CurrentWorkingDir->SetDirectory(Buffer2), S_OK);
229     test_at_end(EnumStr);
230 
231     Buffer[0] = '\0';
232     if (g_WinVersion < WINVER_VISTA)
233     {
234         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), E_NOTIMPL);
235     }
236     else
237     {
238         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), S_OK);
239         ok(!wcscmp(Buffer2, Buffer), "Expected %s, got %s\n", wine_dbgstr_w(Buffer2), wine_dbgstr_w(Buffer));
240     }
241 
242     Buffer2[2] = '\0';
243     ok_hr(hr = CurrentWorkingDir->SetDirectory(Buffer2), S_OK);
244     test_at_end(EnumStr);
245 
246     Buffer[0] = '\0';
247     Buffer2[2] = '\\';
248     if (g_WinVersion < WINVER_VISTA)
249     {
250         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), E_NOTIMPL);
251     }
252     else
253     {
254         ok_hr(hr = CurrentWorkingDir->GetDirectory(Buffer, _ARRAYSIZE(Buffer)), S_OK);
255         ok(!wcscmp(Buffer2, Buffer), "Expected %s, got %s\n", wine_dbgstr_w(Buffer2), wine_dbgstr_w(Buffer));
256     }
257 
258     ok_hr(hr = ACList->Expand(Buffer2), S_OK);
259     // The first set of results are absolute paths, without hidden files?!
260     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer2, IgnoreHidden);
261     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer2, IgnoreHidden | IgnoreRoot | CheckLast);
262 }
263 
264 static void
265 test_ACListISF_MYCOMPUTER()
266 {
267     CComPtr<IACList2> ACList;
268     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IACList2, &ACList));
269     ok_hr(hr, S_OK);
270     if (!SUCCEEDED(hr))
271         return;
272 
273     // Check the default
274     DWORD CurrentOption = 0xdeadbeef;
275     ok_hr(ACList->GetOptions(&CurrentOption), S_OK);
276     ok(CurrentOption == (ACLO_CURRENTDIR|ACLO_MYCOMPUTER), "Expected the default to be %x, was %lx\n",
277         (ACLO_CURRENTDIR|ACLO_MYCOMPUTER), CurrentOption);
278 
279 
280     CComPtr<IEnumString> EnumStr;
281     ok_hr(hr = ACList->QueryInterface(IID_IEnumString, (void**)&EnumStr), S_OK);
282     if (!SUCCEEDED(hr))
283         return;
284 
285     CComPtr<IPersistFolder> PersistFolder;
286     ok_hr(hr = EnumStr->QueryInterface(IID_IPersistFolder, (void**)&PersistFolder), S_OK);
287     if (!SUCCEEDED(hr))
288         return;
289 
290     CComHeapPtr<ITEMIDLIST> pidlMyComputer;
291     ok_hr(hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlMyComputer), S_OK);
292     if (!SUCCEEDED(hr))
293         return;
294 
295 
296     hr = EnumStr->Reset();
297     if (g_WinVersion < WINVER_VISTA)
298         ok_hr(hr, S_FALSE);
299     else
300         ok_hr(hr, S_OK);
301     test_ExpectDrives(EnumStr, pidlMyComputer);
302 
303     ok_hr(hr = ACList->SetOptions(ACLO_MYCOMPUTER), S_OK);
304     ok_hr(EnumStr->Reset(), S_OK);
305     test_ExpectDrives(EnumStr, pidlMyComputer);
306 
307     WCHAR Buffer[MAX_PATH];
308     GetSystemWindowsDirectoryW(Buffer, _ARRAYSIZE(Buffer));
309     // Windows 2k3 does not parse it without the trailing '\\'
310     Buffer[3] = '\0';
311     CComHeapPtr<ITEMIDLIST> pidlDiskRoot;
312     ok_hr(hr = SHParseDisplayName(Buffer, NULL, &pidlDiskRoot, NULL, NULL), S_OK);
313     if (!SUCCEEDED(hr))
314         return;
315     Buffer[2] = '\0';
316 
317     ok_hr(hr = ACList->Expand(Buffer), S_OK);
318     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, None);
319     test_ExpectDrives(EnumStr, pidlMyComputer);
320 
321     ok_hr(hr = ACList->Expand(Buffer), S_OK);
322     ok_hr(EnumStr->Reset(), S_OK);
323     // Pre vista does not remove the expanded data from the enumeration, it changes it to relative paths???
324     if (g_WinVersion < WINVER_VISTA)
325         test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, IgnoreRoot);
326     test_ExpectDrives(EnumStr, pidlMyComputer);
327 
328     ok_hr(EnumStr->Reset(), S_OK);
329     ok_hr(hr = ACList->Expand(Buffer), S_OK);
330     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, None);
331     test_ExpectDrives(EnumStr, pidlMyComputer);
332 }
333 
334 static void
335 test_ACListISF_DESKTOP()
336 {
337     CComPtr<IEnumString> EnumStr;
338     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
339     ok_hr(hr, S_OK);
340     if (!SUCCEEDED(hr))
341         return;
342 
343     CComPtr<IACList2> ACList;
344     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
345     if (!SUCCEEDED(hr))
346         return;
347 
348     ok_hr(hr = ACList->SetOptions(ACLO_DESKTOP), S_OK);
349     test_ExpectFolders(EnumStr, NULL, NULL, IgnoreRoot | CheckLast | IgnoreHidden);
350 }
351 
352 static void
353 test_ACListISF_FAVORITES()
354 {
355     CComPtr<IEnumString> EnumStr;
356     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
357     ok_hr(hr, S_OK);
358     if (!SUCCEEDED(hr))
359         return;
360 
361     CComPtr<IACList2> ACList;
362     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
363     if (!SUCCEEDED(hr))
364         return;
365 
366     CComHeapPtr<ITEMIDLIST> pidlFavorites;
367     ok_hr(hr = SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidlFavorites), S_OK);
368     if (!SUCCEEDED(hr))
369         return;
370 
371     ok_hr(hr = ACList->SetOptions(ACLO_FAVORITES), S_OK);
372     test_ExpectFolders(EnumStr, pidlFavorites, NULL, IgnoreRoot | CheckLast | IgnoreHidden);
373 }
374 
375 static void
376 test_ACListISF_FILESYSONLY()
377 {
378     CComPtr<IEnumString> EnumStr;
379     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
380     ok_hr(hr, S_OK);
381     if (!SUCCEEDED(hr))
382         return;
383 
384     CComPtr<IACList2> ACList;
385     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
386     if (!SUCCEEDED(hr))
387         return;
388 
389     WCHAR Buffer[MAX_PATH];
390     GetSystemWindowsDirectoryW(Buffer, _ARRAYSIZE(Buffer));
391     // Windows 2k3 does not parse it without the trailing '\\'
392     Buffer[3] = '\0';
393     CComHeapPtr<ITEMIDLIST> pidlDiskRoot;
394     ok_hr(hr = SHParseDisplayName(Buffer, NULL, &pidlDiskRoot, NULL, NULL), S_OK);
395     if (!SUCCEEDED(hr))
396         return;
397 
398     ok_hr(hr = ACList->SetOptions(ACLO_FILESYSONLY), S_OK);
399     test_at_end(EnumStr);
400 
401     ok_hr(hr = ACList->Expand(Buffer), S_OK);
402     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, CheckLast | IgnoreHidden);
403 }
404 
405 static void
406 test_ACListISF_FILESYSDIRS()
407 {
408     CComPtr<IEnumString> EnumStr;
409     HRESULT hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_ALL, IID_PPV_ARG(IEnumString, &EnumStr));
410     ok_hr(hr, S_OK);
411     if (!SUCCEEDED(hr))
412         return;
413 
414     CComPtr<IACList2> ACList;
415     ok_hr(hr = EnumStr->QueryInterface(IID_IACList2, (void**)&ACList), S_OK);
416     if (!SUCCEEDED(hr))
417         return;
418 
419     WCHAR Buffer[MAX_PATH];
420     GetSystemWindowsDirectoryW(Buffer, _ARRAYSIZE(Buffer));
421     // Windows 2k3 does not parse it without the trailing '\\'
422     Buffer[3] = '\0';
423     CComHeapPtr<ITEMIDLIST> pidlDiskRoot;
424     ok_hr(hr = SHParseDisplayName(Buffer, NULL, &pidlDiskRoot, NULL, NULL), S_OK);
425     if (!SUCCEEDED(hr))
426         return;
427 
428     ok_hr(hr = ACList->SetOptions(ACLO_FILESYSDIRS), S_OK);
429     test_at_end(EnumStr);
430 
431     ok_hr(hr = ACList->Expand(Buffer), S_OK);
432     test_ExpectFolders(EnumStr, pidlDiskRoot, Buffer, CheckLast | IgnoreFiles | IgnoreHidden);
433 }
434 
435 static void GetEnvStatus()
436 {
437     RTL_OSVERSIONINFOEXW rtlinfo = {0};
438     void (__stdcall* pRtlGetVersion)(RTL_OSVERSIONINFOEXW*);
439     pRtlGetVersion = (void (__stdcall*)(RTL_OSVERSIONINFOEXW*))GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");
440 
441     rtlinfo.dwOSVersionInfoSize = sizeof(rtlinfo);
442     pRtlGetVersion(&rtlinfo);
443     g_WinVersion = (rtlinfo.dwMajorVersion << 8) | rtlinfo.dwMinorVersion;
444 
445     SHELLFLAGSTATE sfs = {0};
446     SHGetSettings(&sfs, SSF_SHOWALLOBJECTS);
447     g_ShowHidden = !!sfs.fShowAllObjects;
448     trace("Show hidden folders: %s\n", g_ShowHidden ? "yes" : "no");
449 }
450 
451 struct CCoInit
452 {
453     CCoInit() { hres = CoInitialize(NULL); }
454     ~CCoInit() { if (SUCCEEDED(hres)) { CoUninitialize(); } }
455     HRESULT hres;
456 };
457 
458 START_TEST(ACListISF)
459 {
460     GetEnvStatus();
461     CCoInit init;
462     ok_hr(init.hres, S_OK);
463     if (!SUCCEEDED(init.hres))
464         return;
465 
466     test_ACListISF_NONE();
467     test_ACListISF_CURRENTDIR();
468     test_ACListISF_MYCOMPUTER();
469     test_ACListISF_DESKTOP();
470     test_ACListISF_FAVORITES();
471     test_ACListISF_FILESYSONLY();
472     test_ACListISF_FILESYSDIRS();
473 }
474