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