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