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 } 467