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