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