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)
9 #include <ndk/rtlfuncs.h>
11 #include <shellutils.h>
14 static DWORD g_WinVersion
;
16 typedef HRESULT (WINAPI
*tSHCreateDataObject
)(PCIDLIST_ABSOLUTE pidlFolder
, UINT cidl
, PCUITEMID_CHILD_ARRAY apidl
, IDataObject
*pdtInner
, REFIID riid
, void **ppv
);
17 static tSHCreateDataObject pSHCreateDataObject
;
20 static void TestAdviseAndCanonical(PCIDLIST_ABSOLUTE pidlFolder
, UINT cidl
, PCUIDLIST_RELATIVE_ARRAY apidl
)
22 CComPtr
<IDataObject
> spDataObj
;
23 HRESULT hr
= pSHCreateDataObject(pidlFolder
, cidl
, apidl
, NULL
, IID_PPV_ARG(IDataObject
, &spDataObj
));
29 hr
= spDataObj
->DAdvise(NULL
, 0, NULL
, NULL
);
30 ok_hex(hr
, OLE_E_ADVISENOTSUPPORTED
);
32 hr
= spDataObj
->DUnadvise(0);
33 ok_hex(hr
, OLE_E_ADVISENOTSUPPORTED
);
35 hr
= spDataObj
->EnumDAdvise(NULL
);
36 ok_hex(hr
, OLE_E_ADVISENOTSUPPORTED
);
39 FORMATETC in
= {1, (DVTARGETDEVICE
*)2, 3, 4, 5};
40 FORMATETC out
= {6, (DVTARGETDEVICE
*)7, 8, 9, 10};
42 hr
= spDataObj
->GetCanonicalFormatEtc(&in
, &out
);
43 ok_hex(hr
, DATA_S_SAMEFORMATETC
);
45 if (g_WinVersion
< _WIN32_WINNT_VISTA
)
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");
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");
66 static inline PCUIDLIST_ABSOLUTE
HIDA_GetPIDLFolder(CIDA
const* pida
)
68 return (PCUIDLIST_ABSOLUTE
)(((LPBYTE
)pida
) + (pida
)->aoffset
[0]);
71 static inline PCUIDLIST_RELATIVE
HIDA_GetPIDLItem(CIDA
const* pida
, SIZE_T i
)
73 return (PCUIDLIST_RELATIVE
)(((LPBYTE
)pida
) + (pida
)->aoffset
[i
+ 1]);
76 #define ok_wstri(x, y) \
77 ok(wcsicmp(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x)
79 static void TestHIDA(PVOID pData
, SIZE_T Size
, LPCWSTR ExpectRoot
, LPCWSTR ExpectPath1
, LPCWSTR ExpectPath2
)
81 LPIDA pida
= (LPIDA
)pData
;
83 ok_int(pida
->cidl
, 2);
87 WCHAR FolderPath
[MAX_PATH
], Item1
[MAX_PATH
], Item2
[MAX_PATH
];
88 BOOL bRet
= SHGetPathFromIDListW(HIDA_GetPIDLFolder(pida
), FolderPath
);
92 ok_wstri(FolderPath
, ExpectRoot
);
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)));
97 bRet
= SHGetPathFromIDListW(pidl1
, Item1
);
101 ok_wstri(Item1
, ExpectPath1
);
103 bRet
= SHGetPathFromIDListW(pidl2
, Item2
);
107 ok_wstri(Item2
, ExpectPath2
);
110 static void TestHDROP(PVOID pData
, SIZE_T Size
, LPCWSTR ExpectRoot
, LPCWSTR ExpectPath1
, LPCWSTR ExpectPath2
)
112 DROPFILES
* pDropFiles
= (DROPFILES
*)pData
;
113 ok_int(pDropFiles
->fWide
, TRUE
);
115 LPCWSTR Expected
[2] = { ExpectPath1
, ExpectPath2
};
117 DWORD offset
= pDropFiles
->pFiles
;
121 LPCWSTR ptr
= (LPCWSTR
)(((BYTE
*)pDropFiles
) + offset
);
125 if (Count
< _countof(Expected
))
126 ok_wstri(Expected
[Count
], ptr
);
128 offset
+= (wcslen(ptr
) + 1) * sizeof(WCHAR
);
133 static void TestFilenameA(PVOID pData
, SIZE_T Size
, LPCWSTR ExpectRoot
, LPCWSTR ExpectPath1
, LPCWSTR ExpectPath2
)
135 LPCSTR FirstFile
= (LPCSTR
)pData
;
138 HRESULT hr
= SHStrDupA(FirstFile
, &FirstFileW
);
143 ok_wstri(ExpectPath1
, FirstFileW
);
144 CoTaskMemFree(FirstFileW
);
147 static void TestFilenameW(PVOID pData
, SIZE_T Size
, LPCWSTR ExpectRoot
, LPCWSTR ExpectPath1
, LPCWSTR ExpectPath2
)
149 LPCWSTR FirstFile
= (LPCWSTR
)pData
;
150 ok_wstri(ExpectPath1
, FirstFile
);
154 static void TestDefaultFormat(PCIDLIST_ABSOLUTE pidlFolder
, UINT cidl
, PCUIDLIST_RELATIVE_ARRAY apidl
)
156 CComPtr
<IDataObject
> spDataObj
;
157 HRESULT hr
= pSHCreateDataObject(pidlFolder
, cidl
, apidl
, NULL
, IID_PPV_ARG(IDataObject
, &spDataObj
));
163 CComPtr
<IEnumFORMATETC
> pEnumFmt
;
164 hr
= spDataObj
->EnumFormatEtc(DATADIR_GET
, &pEnumFmt
);
171 RegisterClipboardFormatA(CFSTR_SHELLIDLISTA
),
173 RegisterClipboardFormatA(CFSTR_FILENAMEA
),
174 RegisterClipboardFormatA("FileNameW"),
179 while (S_OK
== (hr
=pEnumFmt
->Next(1, &fmt
, NULL
)))
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
))
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
);
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
);
198 trace("Got %u formats\n", Count
);
199 ULONG ExpectedCount
= (g_WinVersion
< _WIN32_WINNT_WIN8
) ? 1 : 4;
200 ok_int(Count
, (int)ExpectedCount
);
203 typedef void (*TestFunction
)(PVOID pData
, SIZE_T Size
, LPCWSTR ExpectRoot
, LPCWSTR ExpectPath1
, LPCWSTR ExpectPath2
);
204 TestFunction TestFormats
[] = {
211 WCHAR ExpectRoot
[MAX_PATH
], ExpectItem1
[MAX_PATH
], ExpectItem2
[MAX_PATH
];
213 hr
= SHGetFolderPathW(NULL
, CSIDL_WINDOWS
, NULL
, 0, ExpectRoot
);
218 hr
= SHGetFolderPathW(NULL
, CSIDL_SYSTEM
, NULL
, 0, ExpectItem1
);
223 hr
= SHGetFolderPathW(NULL
, CSIDL_RESOURCES
, NULL
, 0, ExpectItem2
);
229 /* The formats are not synthesized on request */
230 for (Count
= 0; Count
< _countof(Expected
); ++Count
)
232 STGMEDIUM medium
= {0};
233 FORMATETC etc
= { (CLIPFORMAT
)Expected
[Count
], NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
234 char szExpected
[512];
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
);
241 if (Count
< ExpectedCount
)
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
)
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
);
255 if (g_WinVersion
< _WIN32_WINNT_VISTA
)
256 ok(hr
== E_INVALIDARG
, "0x%x (0x%x(%s))\n", (unsigned int)hr
, Expected
[Count
], szExpected
);
258 ok(hr
== DV_E_FORMATETC
, "0x%x (0x%x(%s))\n", (unsigned int)hr
, Expected
[Count
], szExpected
);
262 ReleaseStgMedium(&medium
);
265 CLIPFORMAT Format
= RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECTW
);
266 FORMATETC formatetc
= { Format
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
269 hr
= spDataObj
->GetData(&formatetc
, &medium
);
270 if (g_WinVersion
< _WIN32_WINNT_VISTA
)
271 ok_hex(hr
, E_INVALIDARG
);
273 ok_hex(hr
, DV_E_FORMATETC
);
277 static void TestSetAndGetExtraFormat(PCIDLIST_ABSOLUTE pidlFolder
, UINT cidl
, PCUIDLIST_RELATIVE_ARRAY apidl
)
279 CComPtr
<IDataObject
> spDataObj
;
280 HRESULT hr
= pSHCreateDataObject(pidlFolder
, cidl
, apidl
, NULL
, IID_PPV_ARG(IDataObject
, &spDataObj
));
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
);
292 GlobalUnlock(medium
.hGlobal
);
294 UINT flags
= GlobalFlags(medium
.hGlobal
);
295 SIZE_T size
= GlobalSize(medium
.hGlobal
);
297 ok_size_t(size
, sizeof(DWORD
));
299 FORMATETC etc
= { (CLIPFORMAT
)RegisterClipboardFormatA(CFSTR_INDRAGLOOPA
), NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
300 FORMATETC etc2
= etc
;
303 hr
= spDataObj
->SetData(&etc
, &medium
, FALSE
);
304 if (g_WinVersion
< _WIN32_WINNT_WIN8
)
305 ok_hex(hr
, E_INVALIDARG
);
307 ok_hex(hr
, E_NOTIMPL
);
309 /* Object takes ownership! */
310 hr
= spDataObj
->SetData(&etc
, &medium
, TRUE
);
315 /* Does not touch the hGlobal! */
316 flags
= GlobalFlags(medium
.hGlobal
);
317 size
= GlobalSize(medium
.hGlobal
);
319 ok_size_t(size
, sizeof(DWORD
));
321 STGMEDIUM medium2
= {0};
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
);
331 ok_hex(hr
, DV_E_FORMATETC
);
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
);
341 ok_hex(hr
, DV_E_FORMATETC
);
342 etc2
.tymed
= TYMED_HGLOBAL
;
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
);
351 ok_hex(medium2
.tymed
, TYMED_HGLOBAL
);
352 if (g_WinVersion
< _WIN32_WINNT_VISTA
)
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
);
361 ok_ptr(medium2
.pUnkForRelease
, NULL
);
362 ok(medium
.hGlobal
!= medium2
.hGlobal
, "Pointers are the same!\n");
365 flags
= GlobalFlags(medium2
.hGlobal
);
366 size
= GlobalSize(medium2
.hGlobal
);
368 ok_size_t(size
, sizeof(DWORD
));
370 data
= (PDWORD
)GlobalLock(medium2
.hGlobal
);
372 ok_int(*data
, 12345);
374 ok(0, "GlobalLock: %lu\n", GetLastError());
375 GlobalUnlock(medium2
.hGlobal
);
377 HGLOBAL backup
= medium2
.hGlobal
;
378 ReleaseStgMedium(&medium2
);
380 flags
= GlobalFlags(backup
);
381 size
= GlobalSize(backup
);
382 if (g_WinVersion
< _WIN32_WINNT_VISTA
)
384 /* Same object! just the pUnkForRelease was set, so original hGlobal is still valid */
386 ok_size_t(size
, sizeof(DWORD
));
390 ok_hex(flags
, GMEM_INVALID_HANDLE
);
394 /* Original is still intact (but no longer ours!) */
395 flags
= GlobalFlags(medium
.hGlobal
);
396 size
= GlobalSize(medium
.hGlobal
);
398 ok_size_t(size
, sizeof(DWORD
));
401 HGLOBAL backup
= medium
.hGlobal
;
404 /* Now our hGlobal is deleted */
405 flags
= GlobalFlags(backup
);
406 size
= GlobalSize(backup
);
407 ok_hex(flags
, GMEM_INVALID_HANDLE
);
411 START_TEST(SHCreateDataObject
)
415 pSHCreateDataObject
= (tSHCreateDataObject
)GetProcAddress(GetModuleHandleA("shell32.dll"), "SHCreateDataObject");
416 if (!pSHCreateDataObject
)
418 skip("shell32!SHCreateDataObject not exported\n");
422 CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
424 RTL_OSVERSIONINFOEXW rtlinfo
= {0};
426 rtlinfo
.dwOSVersionInfoSize
= sizeof(rtlinfo
);
427 RtlGetVersion((PRTL_OSVERSIONINFOW
)&rtlinfo
);
428 g_WinVersion
= (rtlinfo
.dwMajorVersion
<< 8) | rtlinfo
.dwMinorVersion
;
430 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidlWindows
;
431 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidlSystem32
;
432 CComHeapPtr
<ITEMIDLIST_ABSOLUTE
> pidlResources
;
434 hr
= SHGetFolderLocation(NULL
, CSIDL_WINDOWS
, NULL
, 0, &pidlWindows
);
439 hr
= SHGetFolderLocation(NULL
, CSIDL_SYSTEM
, NULL
, 0, &pidlSystem32
);
444 hr
= SHGetFolderLocation(NULL
, CSIDL_RESOURCES
, NULL
, 0, &pidlResources
);
449 CComPtr
<IShellFolder
> shellFolder
;
450 PCUITEMID_CHILD child1
;
451 hr
= SHBindToParent(pidlSystem32
, IID_PPV_ARG(IShellFolder
, &shellFolder
), &child1
);
456 PCUITEMID_CHILD child2
= ILFindLastID(pidlResources
);
459 PCUIDLIST_RELATIVE apidl
[2] = {
463 TestAdviseAndCanonical(pidlWindows
, cidl
, apidl
);
464 TestDefaultFormat(pidlWindows
, cidl
, apidl
);
465 TestSetAndGetExtraFormat(pidlWindows
, cidl
, apidl
);