1 /*
2  * PROJECT:     ReactOS api tests
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Test for CIDLDataObj
5  * COPYRIGHT:   Copyright 2019 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "shelltest.h"
9 #include <ndk/rtlfuncs.h>
10 #include <stdio.h>
11 #include <shellutils.h>
12 #include <shlwapi.h>
13 
14 static DWORD g_WinVersion;
15 
16 
17 static void TestAdviseAndCanonical(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl)
18 {
19     CComPtr<IDataObject> spDataObj;
20     HRESULT hr = CIDLData_CreateFromIDArray(pidlFolder, cidl, apidl, &spDataObj);
21 
22     ok_hex(hr, S_OK);
23     if (!SUCCEEDED(hr))
24         return;
25 
26     hr = spDataObj->DAdvise(NULL, 0, NULL, NULL);
27     ok_hex(hr, OLE_E_ADVISENOTSUPPORTED);
28 
29     hr = spDataObj->DUnadvise(0);
30     ok_hex(hr, OLE_E_ADVISENOTSUPPORTED);
31 
32     hr = spDataObj->EnumDAdvise(NULL);
33     ok_hex(hr, OLE_E_ADVISENOTSUPPORTED);
34 
35 
36     FORMATETC in = {1, (DVTARGETDEVICE*)2, 3, 4, 5};
37     FORMATETC out = {6, (DVTARGETDEVICE*)7, 8, 9, 10};
38 
39     hr = spDataObj->GetCanonicalFormatEtc(&in, &out);
40     ok_hex(hr, DATA_S_SAMEFORMATETC);
41 
42     if (g_WinVersion < _WIN32_WINNT_VISTA)
43     {
44         ok_int(out.cfFormat, 6);
45         ok_ptr(out.ptd, (void*)7);
46         ok_int(out.dwAspect, 8);
47         ok_int(out.lindex, 9);
48         ok_int(out.tymed, 10);
49         trace("out unmodified\n");
50     }
51     else
52     {
53         ok_int(out.cfFormat, in.cfFormat);
54         ok_ptr(out.ptd, NULL);
55         ok_int(out.dwAspect, (int)in.dwAspect);
56         ok_int(out.lindex, in.lindex);
57         ok_int(out.tymed, (int)in.tymed);
58         trace("in copied to out\n");
59     }
60 }
61 
62 
63 #define ok_wstri(x, y) \
64     ok(_wcsicmp(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x)
65 
66 static void TestHIDA(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2)
67 {
68     LPIDA pida = (LPIDA)pData;
69 
70     ok_int(pida->cidl, 2);
71     if (pida->cidl != 2)
72         return;
73 
74     WCHAR FolderPath[MAX_PATH], Item1[MAX_PATH], Item2[MAX_PATH];
75     BOOL bRet = SHGetPathFromIDListW(HIDA_GetPIDLFolder(pida), FolderPath);
76     ok_int(bRet, TRUE);
77     if (!bRet)
78         return;
79     ok_wstri(FolderPath, ExpectRoot);
80 
81     CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl1(ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, 0)));
82     CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl2(ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, 1)));
83 
84     bRet = SHGetPathFromIDListW(pidl1, Item1);
85     ok_int(bRet, TRUE);
86     if (!bRet)
87         return;
88     ok_wstri(Item1, ExpectPath1);
89 
90     bRet = SHGetPathFromIDListW(pidl2, Item2);
91     ok_int(bRet, TRUE);
92     if (!bRet)
93         return;
94     ok_wstri(Item2, ExpectPath2);
95 }
96 
97 static void TestHDROP(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2)
98 {
99     DROPFILES* pDropFiles = (DROPFILES*)pData;
100     ok_int(pDropFiles->fWide, TRUE);
101 
102     LPCWSTR Expected[2] = { ExpectPath1, ExpectPath2 };
103 
104     SIZE_T offset = pDropFiles->pFiles;
105     UINT Count = 0;
106     for (;;Count++)
107     {
108         LPCWSTR ptr = (LPCWSTR)(((BYTE*)pDropFiles) + offset);
109         if (!*ptr)
110             break;
111 
112         if (Count < _countof(Expected))
113             ok_wstri(Expected[Count], ptr);
114 
115         offset += (wcslen(ptr) + 1) * sizeof(WCHAR);
116     }
117     ok_int(Count, 2);
118 }
119 
120 static void TestFilenameA(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2)
121 {
122     LPCSTR FirstFile = (LPCSTR)pData;
123     LPWSTR FirstFileW;
124 
125     HRESULT hr = SHStrDupA(FirstFile, &FirstFileW);
126     ok_hex(hr, S_OK);
127     if (!SUCCEEDED(hr))
128         return;
129 
130     ok_wstri(ExpectPath1, FirstFileW);
131     CoTaskMemFree(FirstFileW);
132 }
133 
134 static void TestFilenameW(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2)
135 {
136     LPCWSTR FirstFile = (LPCWSTR)pData;
137     ok_wstri(ExpectPath1, FirstFile);
138 }
139 
140 
141 static void TestDefaultFormat(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl)
142 {
143     CComPtr<IDataObject> spDataObj;
144     HRESULT hr = CIDLData_CreateFromIDArray(pidlFolder, cidl, apidl, &spDataObj);
145 
146     ok_hex(hr, S_OK);
147     if (!SUCCEEDED(hr))
148         return;
149 
150     CComPtr<IEnumFORMATETC> pEnumFmt;
151     hr = spDataObj->EnumFormatEtc(DATADIR_GET, &pEnumFmt);
152 
153     ok_hex(hr, S_OK);
154     if (!SUCCEEDED(hr))
155         return;
156 
157     UINT Expected[4] = {
158         RegisterClipboardFormatA(CFSTR_SHELLIDLISTA),
159         CF_HDROP,
160         RegisterClipboardFormatA(CFSTR_FILENAMEA),
161         RegisterClipboardFormatA("FileNameW"),
162     };
163 
164     UINT Count = 0;
165     FORMATETC fmt;
166     while (S_OK == (hr=pEnumFmt->Next(1, &fmt, NULL)))
167     {
168         char szGot[512], szExpected[512];
169         GetClipboardFormatNameA(fmt.cfFormat, szGot, sizeof(szGot));
170         ok(Count < _countof(Expected), "%u\n", Count);
171         if (Count < _countof(Expected))
172         {
173             GetClipboardFormatNameA(Expected[Count], szExpected, sizeof(szExpected));
174             ok(fmt.cfFormat == Expected[Count], "Got 0x%x(%s), expected 0x%x(%s) for %u\n",
175                fmt.cfFormat, szGot, Expected[Count], szExpected, Count);
176         }
177 
178         ok(fmt.ptd == NULL, "Got 0x%p, expected 0x%p for [%u].ptd\n", fmt.ptd, (void*)NULL, Count);
179         ok(fmt.dwAspect == DVASPECT_CONTENT, "Got 0x%lu, expected 0x%d for [%u].dwAspect\n", fmt.dwAspect, DVASPECT_CONTENT, Count);
180         ok(fmt.lindex == -1, "Got 0x%lx, expected 0x%x for [%u].lindex\n", fmt.lindex, -1, Count);
181         ok(fmt.tymed == TYMED_HGLOBAL, "Got 0x%lu, expected 0x%d for [%u].tymed\n", fmt.tymed, TYMED_HGLOBAL, Count);
182 
183         Count++;
184     }
185     trace("Got %u formats\n", Count);
186     ULONG ExpectedCount = (g_WinVersion < _WIN32_WINNT_WIN8) ? 1 : 4;
187     ok_int(Count, (int)ExpectedCount);
188     ok_hex(hr, S_FALSE);
189 
190     typedef void (*TestFunction)(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2);
191     TestFunction TestFormats[] = {
192         TestHIDA,
193         TestHDROP,
194         TestFilenameA,
195         TestFilenameW,
196     };
197 
198     WCHAR ExpectRoot[MAX_PATH], ExpectItem1[MAX_PATH], ExpectItem2[MAX_PATH];
199 
200     hr = SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, ExpectRoot);
201     ok_hex(hr, S_OK);
202     if (!SUCCEEDED(hr))
203         return;
204 
205     hr = SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, 0, ExpectItem1);
206     ok_hex(hr, S_OK);
207     if (!SUCCEEDED(hr))
208         return;
209 
210     hr = SHGetFolderPathW(NULL, CSIDL_RESOURCES, NULL, 0, ExpectItem2);
211     ok_hex(hr, S_OK);
212     if (!SUCCEEDED(hr))
213         return;
214 
215 
216     /* The formats are not synthesized on request */
217     for (Count = 0; Count < _countof(Expected); ++Count)
218     {
219         STGMEDIUM medium = {0};
220         FORMATETC etc = { (CLIPFORMAT)Expected[Count], NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
221         char szExpected[512];
222 
223         GetClipboardFormatNameA(etc.cfFormat, szExpected, sizeof(szExpected));
224         hr = spDataObj->GetData(&etc, &medium);
225         HRESULT hr2 = spDataObj->QueryGetData(&etc);
226         ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE);
227 
228         if (Count < ExpectedCount)
229         {
230             ok(hr == S_OK, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected);
231             ok(medium.tymed == TYMED_HGLOBAL, "0x%lx (0x%x(%s))\n", medium.tymed, Expected[Count], szExpected);
232             if (hr == S_OK && medium.tymed == TYMED_HGLOBAL)
233             {
234                 PVOID pData = GlobalLock(medium.hGlobal);
235                 SIZE_T Size = GlobalSize(medium.hGlobal);
236                 TestFormats[Count](pData, Size, ExpectRoot, ExpectItem1, ExpectItem2);
237                 GlobalUnlock(medium.hGlobal);
238             }
239         }
240         else
241         {
242             if (g_WinVersion < _WIN32_WINNT_VISTA)
243                 ok(hr == E_INVALIDARG, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected);
244             else
245                 ok(hr == DV_E_FORMATETC, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected);
246         }
247 
248         if (SUCCEEDED(hr))
249             ReleaseStgMedium(&medium);
250     }
251 
252     // Not registered
253     CLIPFORMAT Format = RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECTW);
254     FORMATETC formatetc = { Format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
255     STGMEDIUM medium;
256 
257     hr = spDataObj->GetData(&formatetc, &medium);
258     if (g_WinVersion < _WIN32_WINNT_VISTA)
259         ok_hex(hr, E_INVALIDARG);
260     else
261         ok_hex(hr, DV_E_FORMATETC);
262 }
263 
264 
265 static void TestSetAndGetExtraFormat(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl)
266 {
267     CComPtr<IDataObject> spDataObj;
268     HRESULT hr = CIDLData_CreateFromIDArray(pidlFolder, cidl, apidl, &spDataObj);
269 
270     ok_hex(hr, S_OK);
271     if (!SUCCEEDED(hr))
272         return;
273 
274     STGMEDIUM medium = {0};
275     medium.tymed = TYMED_HGLOBAL;
276     medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD));
277     ok(medium.hGlobal != NULL, "Download more ram\n");
278     PDWORD data = (PDWORD)GlobalLock(medium.hGlobal);
279     *data = 12345;
280     GlobalUnlock(medium.hGlobal);
281 
282     UINT flags = GlobalFlags(medium.hGlobal);
283     SIZE_T size = GlobalSize(medium.hGlobal);
284     ok_hex(flags, 0);
285     ok_size_t(size, sizeof(DWORD));
286 
287     FORMATETC etc = { (CLIPFORMAT)RegisterClipboardFormatA(CFSTR_INDRAGLOOPA), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
288     FORMATETC etc2 = etc;
289 
290     /* Not supported! */
291     hr = spDataObj->SetData(&etc, &medium, FALSE);
292     if (g_WinVersion < _WIN32_WINNT_WIN8)
293         ok_hex(hr, E_INVALIDARG);
294     else
295         ok_hex(hr, E_NOTIMPL);
296 
297     /* Object takes ownership! */
298     hr = spDataObj->SetData(&etc, &medium, TRUE);
299     ok_hex(hr, S_OK);
300     if (!SUCCEEDED(hr))
301         return;
302 
303     /* Does not touch the hGlobal! */
304     flags = GlobalFlags(medium.hGlobal);
305     size = GlobalSize(medium.hGlobal);
306     ok_hex(flags, 0);
307     ok_size_t(size, sizeof(DWORD));
308 
309     STGMEDIUM medium2 = {0};
310 
311     /* No conversion */
312     etc2.dwAspect = DVASPECT_DOCPRINT;
313     hr = spDataObj->GetData(&etc2, &medium2);
314     HRESULT hr2 = spDataObj->QueryGetData(&etc2);
315     ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE);
316     if (g_WinVersion < _WIN32_WINNT_VISTA)
317         ok_hex(hr, E_INVALIDARG);
318     else
319         ok_hex(hr, DV_E_FORMATETC);
320 
321     etc2.dwAspect = DVASPECT_CONTENT;
322     etc2.tymed = TYMED_NULL;
323     hr = spDataObj->GetData(&etc2, &medium2);
324     hr2 = spDataObj->QueryGetData(&etc2);
325     ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE);
326     if (g_WinVersion < _WIN32_WINNT_VISTA)
327         ok_hex(hr, E_INVALIDARG);
328     else
329         ok_hex(hr, DV_E_FORMATETC);
330     etc2.tymed = TYMED_HGLOBAL;
331 
332     ok_ptr(medium2.pUnkForRelease, NULL);
333     hr = spDataObj->GetData(&etc2, &medium2);
334     hr2 = spDataObj->QueryGetData(&etc2);
335     ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE);
336     ok_hex(hr, S_OK);
337     if (hr == S_OK)
338     {
339         ok_hex(medium2.tymed, TYMED_HGLOBAL);
340         if (g_WinVersion < _WIN32_WINNT_VISTA)
341         {
342             /* The IDataObject is set as pUnkForRelease */
343             ok(medium2.pUnkForRelease == (IUnknown*)spDataObj, "Expected the data object (0x%p), got 0x%p\n",
344                 (IUnknown*)spDataObj, medium2.pUnkForRelease);
345             ok(medium.hGlobal == medium2.hGlobal, "Pointers are not the same!, got 0x%p and 0x%p\n", medium.hGlobal, medium2.hGlobal);
346         }
347         else
348         {
349             ok_ptr(medium2.pUnkForRelease, NULL);
350             ok(medium.hGlobal != medium2.hGlobal, "Pointers are the same!\n");
351         }
352 
353         flags = GlobalFlags(medium2.hGlobal);
354         size = GlobalSize(medium2.hGlobal);
355         ok_hex(flags, 0);
356         ok_size_t(size, sizeof(DWORD));
357 
358         data = (PDWORD)GlobalLock(medium2.hGlobal);
359         if (data)
360             ok_int(*data, 12345);
361         else
362             ok(0, "GlobalLock: %lu\n", GetLastError());
363         GlobalUnlock(medium2.hGlobal);
364 
365         HGLOBAL backup = medium2.hGlobal;
366         ReleaseStgMedium(&medium2);
367 
368         flags = GlobalFlags(backup);
369         size = GlobalSize(backup);
370         if (g_WinVersion < _WIN32_WINNT_VISTA)
371         {
372             /* Same object! just the pUnkForRelease was set, so original hGlobal is still valid */
373             ok_hex(flags, 0);
374             ok_size_t(size, sizeof(DWORD));
375         }
376         else
377         {
378             ok_hex(flags, GMEM_INVALID_HANDLE);
379             ok_size_t(size, 0);
380         }
381 
382         /* Original is still intact (but no longer ours!) */
383         flags = GlobalFlags(medium.hGlobal);
384         size = GlobalSize(medium.hGlobal);
385         ok_hex(flags, 0);
386         ok_size_t(size, sizeof(DWORD));
387     }
388 
389     HGLOBAL backup = medium.hGlobal;
390     spDataObj.Release();
391 
392     /* Now our hGlobal is deleted */
393     flags = GlobalFlags(backup);
394     size = GlobalSize(backup);
395     ok_hex(flags, GMEM_INVALID_HANDLE);
396     ok_size_t(size, 0);
397 }
398 
399 START_TEST(CIDLData)
400 {
401     HRESULT hr;
402 
403     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
404 
405     RTL_OSVERSIONINFOEXW rtlinfo = {0};
406 
407     rtlinfo.dwOSVersionInfoSize = sizeof(rtlinfo);
408     RtlGetVersion((PRTL_OSVERSIONINFOW)&rtlinfo);
409     g_WinVersion = (rtlinfo.dwMajorVersion << 8) | rtlinfo.dwMinorVersion;
410 
411     CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlWindows;
412     CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlSystem32;
413     CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlResources;
414 
415     hr = SHGetFolderLocation(NULL, CSIDL_WINDOWS, NULL, 0, &pidlWindows);
416     ok_hex(hr, S_OK);
417     if (!SUCCEEDED(hr))
418         return;
419 
420     hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, 0, &pidlSystem32);
421     ok_hex(hr, S_OK);
422     if (!SUCCEEDED(hr))
423         return;
424 
425     hr = SHGetFolderLocation(NULL, CSIDL_RESOURCES, NULL, 0, &pidlResources);
426     ok_hex(hr, S_OK);
427     if (!SUCCEEDED(hr))
428         return;
429 
430     CComPtr<IShellFolder> shellFolder;
431     PCUITEMID_CHILD child1;
432     hr = SHBindToParent(pidlSystem32, IID_PPV_ARG(IShellFolder, &shellFolder), &child1);
433     ok_hex(hr, S_OK);
434     if (!SUCCEEDED(hr))
435         return;
436 
437     PCUITEMID_CHILD child2 = ILFindLastID(pidlResources);
438 
439     UINT cidl = 2;
440     PCUIDLIST_RELATIVE apidl[2] = {
441         child1, child2
442     };
443 
444     TestAdviseAndCanonical(pidlWindows, cidl, apidl);
445     TestDefaultFormat(pidlWindows, cidl, apidl);
446     TestSetAndGetExtraFormat(pidlWindows, cidl, apidl);
447 }
448