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