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 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
16static DWORD g_WinVersion;
17
18typedef HRESULT(WINAPI* tSHCreateFileDataObject)(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject* pDataInner, IDataObject** ppDataObj);
19static tSHCreateFileDataObject pSHCreateFileDataObject;
20
21
22static 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
71static 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
102static 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
125static 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
139static 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
146static 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
269static 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
403START_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}