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