Reactos
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
14static DWORD g_WinVersion;
15
16
17static 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
66static 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
97static 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
120static 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
134static 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
141static 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
265static 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
399START_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}