1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for CShellDesktop
5  * PROGRAMMER:      Thomas Faber <thomas.faber@reactos.org>
6  *                  Mark Jansen
7  */
8 
9 #include "shelltest.h"
10 
11 #include <ndk/rtlfuncs.h>
12 #include <stdio.h>
13 #include <shellutils.h>
14 
15 // We would normally use S_LESSTHAN and S_GREATERTHAN, but w2k3 returns numbers like 3 and -3...
16 // So instead we check on the sign bit (compare result is the low word of the hresult).
17 #define SHORT_SIGN_BIT  0x8000
18 
19 static
20 VOID
21 compare_imp(IShellFolder* psf, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, HRESULT expected)
22 {
23     HRESULT hr;
24     _SEH2_TRY
25     {
26         hr = psf->CompareIDs(0, pidl1, pidl2);
27     }
28     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
29     {
30         winetest_ok(0, "Exception %lx!\n", _SEH2_GetExceptionCode());
31         hr = HRESULT_FROM_WIN32(RtlNtStatusToDosError(_SEH2_GetExceptionCode()));
32     }
33     _SEH2_END;
34     if (expected == S_LESSTHAN)
35         winetest_ok(SUCCEEDED(hr) && (hr & SHORT_SIGN_BIT), "hr = %lx\n", hr);
36     else if (expected == S_EQUAL)
37         winetest_ok(hr == S_EQUAL, "hr = %lx\n", hr);
38     else if (expected == S_GREATERTHAN)
39         winetest_ok(SUCCEEDED(hr) && !(hr & SHORT_SIGN_BIT), "hr = %lx\n", hr);
40     else
41         winetest_ok(hr == expected, "hr = %lx\n", hr);
42 }
43 
44 // make the winetest_ok look like it came from the line where the compare function was called, and not from inside the compare_imp function :)
45 #define compare         (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : compare_imp
46 
47 static
48 VOID
49 TestCompareIDList(IShellFolder* psf)
50 {
51     compare(psf, NULL, NULL, E_INVALIDARG);
52 
53     CComHeapPtr<ITEMIDLIST> desktop;
54     HRESULT hr = SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, NULL, &desktop);
55     ok(hr == S_OK, "hr = %lx\n", hr);
56     compare(psf, desktop, NULL, E_INVALIDARG);
57     compare(psf, NULL, desktop, E_INVALIDARG);
58     compare(psf, desktop, desktop, S_EQUAL);
59 
60     // First check the ordering of some special folders against eachother
61     CComHeapPtr<ITEMIDLIST> internet;
62     hr = SHGetFolderLocation(NULL, CSIDL_INTERNET, NULL, NULL, &internet);
63     ok(hr == S_OK, "hr = %lx\n", hr);
64     compare(psf, internet, desktop, S_LESSTHAN);
65     compare(psf, desktop, internet, S_GREATERTHAN);
66 
67     CComHeapPtr<ITEMIDLIST> programs;
68     hr = SHGetFolderLocation(NULL, CSIDL_PROGRAMS, NULL, NULL, &programs);
69     ok(hr == S_OK, "hr = %lx\n", hr);
70     compare(psf, programs, desktop, S_LESSTHAN);
71     compare(psf, desktop, programs, S_GREATERTHAN);
72     compare(psf, internet, programs, S_GREATERTHAN);
73     compare(psf, programs, internet, S_LESSTHAN);
74 
75     // Verify that an idlist retrieved from GetCurFolder is equal to the original one.
76     CComPtr<IPersistFolder2> persist;
77     hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist));
78     ok(hr == S_OK, "hr = %lx\n", hr);
79     if (hr == S_OK)
80     {
81         CComHeapPtr<ITEMIDLIST> cur;
82         hr = persist->GetCurFolder(&cur);
83         ok(hr == S_OK, "hr = %lx\n", hr);
84         compare(psf, cur, desktop, S_EQUAL);
85         compare(psf, desktop, cur, S_EQUAL);
86     }
87 
88     // Compare special folders against full paths
89     CComHeapPtr<ITEMIDLIST> dir1, dir2;
90     PathToIDList(L"A:\\AAA.AAA", &dir1);
91     PathToIDList(L"A:\\ZZZ.ZZZ", &dir2);
92 
93     compare(psf, dir1, desktop, S_LESSTHAN);
94     compare(psf, desktop, dir1, S_GREATERTHAN);
95     compare(psf, dir1, programs, S_LESSTHAN);
96     compare(psf, programs, dir1, S_GREATERTHAN);
97     compare(psf, dir1, dir1, S_EQUAL);
98 
99     compare(psf, dir2, desktop, S_LESSTHAN);
100     compare(psf, desktop, dir2, S_GREATERTHAN);
101     compare(psf, dir2, programs, S_LESSTHAN);
102     compare(psf, programs, dir2, S_GREATERTHAN);
103     compare(psf, dir2, dir2, S_EQUAL);
104 
105     CComHeapPtr<ITEMIDLIST> dir3, dir4;
106     PathToIDList(L"Z:\\AAA.AAA", &dir3);
107     PathToIDList(L"Z:\\ZZZ.ZZZ", &dir4);
108 
109     compare(psf, dir3, desktop, S_LESSTHAN);
110     compare(psf, desktop, dir3, S_GREATERTHAN);
111     compare(psf, dir3, programs, S_GREATERTHAN);
112     compare(psf, programs, dir3, S_LESSTHAN);
113     compare(psf, dir3, dir3, S_EQUAL);
114 
115     compare(psf, dir4, desktop, S_LESSTHAN);
116     compare(psf, desktop, dir4, S_GREATERTHAN);
117     compare(psf, dir4, programs, S_GREATERTHAN);
118     compare(psf, programs, dir4, S_LESSTHAN);
119     compare(psf, dir4, dir4, S_EQUAL);
120 
121     // Now compare the paths against eachother.
122     compare(psf, dir1, dir2, S_LESSTHAN);
123     compare(psf, dir2, dir1, S_GREATERTHAN);
124 
125     compare(psf, dir2, dir3, S_LESSTHAN);
126     compare(psf, dir3, dir2, S_GREATERTHAN);
127 
128     compare(psf, dir3, dir4, S_LESSTHAN);
129     compare(psf, dir4, dir3, S_GREATERTHAN);
130 
131     // Check that comparing desktop pidl with another one with another IShellFolder fails
132     CComPtr<IShellFolder> psf2;
133     hr = psf->BindToObject(programs, NULL, IID_IShellFolder, reinterpret_cast<void**>(&psf2));
134     ok(hr == S_OK, "Impossible to bind to Programs pidl");
135     if (hr == S_OK)
136     {
137         // Compare desktop pidl in programs scope should fail since it's relative pidl
138         compare(psf2, desktop, programs, E_INVALIDARG);
139         compare(psf2, programs, desktop, E_INVALIDARG);
140         // For the same reasons, filesystem paths can't be compared with special shell
141         // folders that don't have CFSFolder in children
142         compare(psf2, dir1, dir2, E_INVALIDARG);
143         compare(psf2, dir2, dir1, E_INVALIDARG);
144     }
145 }
146 
147 static
148 VOID
149 TestDesktopFolder(
150     _In_ IShellFolder2 *psf2)
151 {
152     HRESULT hr;
153     CComPtr<IDropTarget> pdt;
154     CComPtr<IDropTarget> pdt_2;
155     CComPtr<IContextMenu> pcm;
156     CComPtr<IContextMenu> pcm_2;
157     CComPtr<IShellView> psv;
158     CComPtr<IShellView> psv_2;
159 
160     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdt));
161     ok(hr == S_OK, "hr = %lx\n", hr);
162 
163     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &pdt_2));
164     ok(hr == S_OK, "hr = %lx\n", hr);
165     ok(pdt != pdt_2, "Expected %p != %p\n", static_cast<PVOID>(pdt), static_cast<PVOID>(pdt_2));
166 
167     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IContextMenu, &pcm));
168     ok(hr == S_OK, "hr = %lx\n", hr);
169 
170     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IContextMenu, &pcm_2));
171     ok(hr == S_OK, "hr = %lx\n", hr);
172     ok(pcm != pcm_2, "Expected %p != %p\n", static_cast<PVOID>(pcm), static_cast<PVOID>(pcm_2));
173 
174     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IShellView, &psv));
175     ok(hr == S_OK, "hr = %lx\n", hr);
176 
177     hr = psf2->CreateViewObject(NULL, IID_PPV_ARG(IShellView, &psv_2));
178     ok(hr == S_OK, "hr = %lx\n", hr);
179     ok(psv != psv_2, "Expected %p != %p\n", static_cast<PVOID>(psv), static_cast<PVOID>(psv_2));
180 
181     STRRET strret;
182     hr = psf2->GetDisplayNameOf(NULL, 0, &strret);
183     ok(hr == S_OK, "hr = %lx\n", hr);
184 }
185 
186 VOID TestInitialize(_In_ IShellFolder *psf)
187 {
188     CComPtr<IPersistFolder2> ppf2;
189     HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
190     ok(hr == S_OK, "hr = %lx\n", hr);
191 
192     /* Create a tiny pidl with no contents */
193     LPITEMIDLIST testpidl = (LPITEMIDLIST)SHAlloc(3 * sizeof(WORD));
194     testpidl->mkid.cb = 2 * sizeof(WORD);
195     *(WORD*)((char*)testpidl + (int)(2 * sizeof(WORD))) = 0;
196 
197     hr = ppf2->Initialize(testpidl);
198     ok(hr == E_INVALIDARG, "hr = %lx\n", hr);
199 
200     //crashes in xp, works on win10
201     //hr = ppf2->Initialize(NULL);
202     //ok(hr == S_OK, "hr = %lx\n", hr);
203     //hr = ppf2->Initialize((LPCITEMIDLIST)0xdeaddead);
204     //ok(hr == S_OK, "hr = %lx\n", hr);
205     //hr = ppf2->GetCurFolder(NULL);
206     //ok(hr == E_INVALIDARG, "hr = %lx\n", hr);
207 
208     CComHeapPtr<ITEMIDLIST> pidl;
209     hr = ppf2->GetCurFolder(&pidl);
210     ok(hr == S_OK, "hr = %lx\n", hr);
211     ok(pidl->mkid.cb == 0, "expected empty pidl got cb = %x\n", pidl->mkid.cb);
212 }
213 
214 START_TEST(CShellDesktop)
215 {
216     HRESULT hr;
217     CComPtr<IShellFolder2> psf2;
218     CComPtr<IShellFolder2> psf2_2;
219     CComPtr<IShellFolder> psf;
220 
221     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
222 
223     hr = CoCreateInstance(CLSID_ShellDesktop,
224                           NULL,
225                           CLSCTX_INPROC_SERVER,
226                           IID_PPV_ARG(IShellFolder2, &psf2));
227     ok(hr == S_OK, "hr = %lx\n", hr);
228     if (FAILED(hr))
229     {
230         skip("Could not instantiate CShellDesktop\n");
231         return;
232     }
233 
234     /* second create should give us a pointer to the same object */
235     hr = CoCreateInstance(CLSID_ShellDesktop,
236                           NULL,
237                           CLSCTX_INPROC_SERVER,
238                           IID_PPV_ARG(IShellFolder2, &psf2_2));
239     ok(hr == S_OK, "hr = %lx\n", hr);
240     ok(psf2 == psf2_2, "Expected %p == %p\n", static_cast<PVOID>(psf2), static_cast<PVOID>(psf2_2));
241 
242     /* SHGetDesktopFolder should also give us the same pointer */
243     hr = SHGetDesktopFolder(&psf);
244     ok(hr == S_OK, "hr = %lx\n", hr);
245     ok(psf == static_cast<IShellFolder *>(psf2), "Expected %p == %p\n", static_cast<PVOID>(psf), static_cast<PVOID>(psf2));
246 
247     TestDesktopFolder(psf2);
248     TestCompareIDList(psf);
249     TestInitialize(psf);
250 }
251