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