Reactos
at master 333 lines 12 kB view raw
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 SHGetAttributesFromDataObject 5 * COPYRIGHT: Copyright 2021 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 15static CLIPFORMAT g_DataObjectAttributes = 0; 16static const DWORD dwDefaultAttributeMask = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE | SFGAO_CANRENAME | 17 SFGAO_CANDELETE | SFGAO_READONLY | SFGAO_STREAM | SFGAO_FOLDER; 18static_assert(dwDefaultAttributeMask == 0x2044003B, "Unexpected default attribute mask"); 19static const DWORD dwDefaultAttributeMask_WS03 = dwDefaultAttributeMask | SFGAO_FILESYSTEM | SFGAO_CAPABILITYMASK; 20static_assert(dwDefaultAttributeMask_WS03 == 0x6044017F, "Unexpected default attribute mask for WS03, Vista"); 21 22struct TmpFile 23{ 24 WCHAR Buffer[MAX_PATH] = {}; 25 26 void Create(LPCWSTR Folder) 27 { 28 GetTempFileNameW(Folder, L"SHG", 0, Buffer); 29 } 30 31 ~TmpFile() 32 { 33 if (Buffer[0]) 34 { 35 SetFileAttributesW(Buffer, FILE_ATTRIBUTE_NORMAL); 36 DeleteFileW(Buffer); 37 } 38 } 39}; 40 41 42CComPtr<IShellFolder> _BindToObject(PCUIDLIST_ABSOLUTE pidl) 43{ 44 CComPtr<IShellFolder> spDesktop, spResult; 45 HRESULT hr = SHGetDesktopFolder(&spDesktop); 46 if (FAILED_UNEXPECTEDLY(hr)) 47 return spResult; 48 49 if (FAILED_UNEXPECTEDLY(spDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &spResult)))) 50 { 51 spResult.Release(); 52 } 53 return spResult; 54} 55 56 57static void ok_attributes_(IDataObject* pDataObject, HRESULT expect_hr, DWORD expect_mask, DWORD expect_attr, UINT expect_items) 58{ 59 FORMATETC fmt = { g_DataObjectAttributes, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; 60 STGMEDIUM medium = {}; 61 62 HRESULT hr = pDataObject->GetData(&fmt, &medium); 63 winetest_ok(hr == expect_hr, "Unexpected result from GetData, got 0x%lx, expected 0x%lx\n", hr, expect_hr); 64 65 if (hr == expect_hr && expect_hr == S_OK) 66 { 67 LPVOID blob = GlobalLock(medium.hGlobal); 68 winetest_ok(blob != nullptr, "Failed to lock hGlobal\n"); 69 if (blob) 70 { 71 SIZE_T size = GlobalSize(medium.hGlobal); 72 winetest_ok(size == 0xc, "Unexpected size, got %lu, expected 12\n", size); 73 if (size == 0xc) 74 { 75 PDWORD data = (PDWORD)blob; 76 winetest_ok(data[0] == expect_mask, "Unexpected mask, got 0x%lx, expected 0x%lx\n", data[0], expect_mask); 77 winetest_ok(data[1] == expect_attr, "Unexpected attr, got 0x%lx, expected 0x%lx\n", data[1], expect_attr); 78 winetest_ok(data[2] == expect_items, "Unexpected item count, got %lu, expected %u\n", data[2], expect_items); 79 } 80 GlobalUnlock(medium.hGlobal); 81 } 82 } 83 84 if (SUCCEEDED(hr)) 85 ReleaseStgMedium(&medium); 86} 87 88 89#define ok_attributes (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_attributes_ 90#define ok_hr_ret(x, y) ok_hr(x, y); if (x != y) return 91 92static void test_SpecialCases() 93{ 94 DWORD dwAttributeMask = 0, dwAttributes = 123; 95 UINT cItems = 123; 96 97 HRESULT hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, &cItems); 98 ok_hr(hr, S_OK); 99 ok_int(dwAttributes, 0); 100 ok_int(cItems, 0); 101 102 cItems = 123; 103 hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, nullptr, &cItems); 104 ok_hr(hr, S_OK); 105 ok_int(cItems, 0); 106 107 dwAttributes = 123; 108 hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, nullptr); 109 ok_hr(hr, S_OK); 110 ok_int(dwAttributes, 0); 111} 112 113 114static void test_AttributesRegistration() 115{ 116 WCHAR Buffer[MAX_PATH] = {}; 117 118 GetModuleFileNameW(NULL, Buffer, _countof(Buffer)); 119 CComHeapPtr<ITEMIDLIST_ABSOLUTE> spPath(ILCreateFromPathW(Buffer)); 120 121 ok(spPath != nullptr, "Unable to create pidl from %S\n", Buffer); 122 if (spPath == nullptr) 123 return; 124 125 SFGAOF attributes = dwDefaultAttributeMask; 126 HRESULT hr; 127 { 128 CComPtr<IShellFolder> spFolder; 129 PCUITEMID_CHILD child; 130 hr = SHBindToParent(spPath, IID_PPV_ARG(IShellFolder, &spFolder), &child); 131 ok_hr_ret(hr, S_OK); 132 133 hr = spFolder->GetAttributesOf(1, &child, &attributes); 134 ok_hr_ret(hr, S_OK); 135 136 if (GetNTVersion() <= _WIN32_WINNT_VISTA) 137 attributes &= dwDefaultAttributeMask_WS03; 138 else 139 attributes &= dwDefaultAttributeMask; 140 } 141 142 CComHeapPtr<ITEMIDLIST> parent(ILClone(spPath)); 143 PCIDLIST_RELATIVE child = ILFindLastID(spPath); 144 ILRemoveLastID(parent); 145 146 CComPtr<IDataObject> spDataObject; 147 hr = CIDLData_CreateFromIDArray(parent, 1, &child, &spDataObject); 148 ok_hr_ret(hr, S_OK); 149 150 /* Not registered yet */ 151 ok_attributes(spDataObject, (GetNTVersion() >= _WIN32_WINNT_VISTA) ? DV_E_FORMATETC : E_INVALIDARG, 0, 0, 0); 152 153 /* Ask for attributes, without specifying any */ 154 DWORD dwAttributeMask = 0, dwAttributes = 0; 155 UINT cItems = 0; 156 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 157 ok_hr(hr, S_OK); 158 159 /* Now there are attributes registered */ 160 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes, 1); 161 162 // Now add an additional mask value (our exe should have a propsheet!) 163 dwAttributeMask = SFGAO_HASPROPSHEET; 164 dwAttributes = 0; 165 cItems = 0; 166 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 167 ok_hr(hr, S_OK); 168 169 // Observe that this is now also cached 170 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask | SFGAO_HASPROPSHEET, attributes | SFGAO_HASPROPSHEET, 1); 171} 172 173static void test_MultipleFiles() 174{ 175 TmpFile TmpFile1, TmpFile2, TmpFile3; 176 177 CComHeapPtr<ITEMIDLIST> pidl_tmpfolder; 178 CComHeapPtr<ITEMIDLIST> pidl1, pidl2, pidl3; 179 180 ITEMIDLIST* items[3] = {}; 181 SFGAOF attributes_first = dwDefaultAttributeMask; 182 SFGAOF attributes2 = dwDefaultAttributeMask; 183 SFGAOF attributes3 = dwDefaultAttributeMask; 184 SFGAOF attributes_last = dwDefaultAttributeMask; 185 186 HRESULT hr; 187 { 188 WCHAR TempFolder[MAX_PATH] = {}; 189 GetTempPathW(_countof(TempFolder), TempFolder); 190 191 // Create temp files 192 TmpFile1.Create(TempFolder); 193 TmpFile2.Create(TempFolder); 194 TmpFile3.Create(TempFolder); 195 196 // Last file is read-only 197 SetFileAttributesW(TmpFile3.Buffer, FILE_ATTRIBUTE_READONLY); 198 199 hr = SHParseDisplayName(TempFolder, NULL, &pidl_tmpfolder, NULL, NULL); 200 ok_hr_ret(hr, S_OK); 201 202 CComPtr<IShellFolder> spFolder = _BindToObject(pidl_tmpfolder); 203 ok(!!spFolder, "Unable to bind to tmp folder\n"); 204 if (!spFolder) 205 return; 206 207 hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile1.Buffer), NULL, &pidl1, NULL); 208 ok_hr_ret(hr, S_OK); 209 210 hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile2.Buffer), NULL, &pidl2, NULL); 211 ok_hr_ret(hr, S_OK); 212 213 hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile3.Buffer), NULL, &pidl3, NULL); 214 ok_hr_ret(hr, S_OK); 215 216 items[0] = pidl1; 217 items[1] = pidl2; 218 items[2] = pidl3; 219 220 // Query file attributes 221 hr = spFolder->GetAttributesOf(1, items, &attributes_first); 222 ok_hr(hr, S_OK); 223 224 hr = spFolder->GetAttributesOf(2, items, &attributes2); 225 ok_hr(hr, S_OK); 226 227 hr = spFolder->GetAttributesOf(3, items, &attributes3); 228 ok_hr(hr, S_OK); 229 230 hr = spFolder->GetAttributesOf(1, items + 2, &attributes_last); 231 ok_hr(hr, S_OK); 232 233 // Ignore any non-default attributes 234 if (GetNTVersion() <= _WIN32_WINNT_VISTA) 235 { 236 attributes_first &= dwDefaultAttributeMask_WS03; 237 attributes2 &= dwDefaultAttributeMask_WS03; 238 attributes3 &= dwDefaultAttributeMask_WS03; 239 attributes_last &= dwDefaultAttributeMask_WS03; 240 } 241 else 242 { 243 attributes_first &= dwDefaultAttributeMask; 244 attributes2 &= dwDefaultAttributeMask; 245 attributes3 &= dwDefaultAttributeMask; 246 attributes_last &= dwDefaultAttributeMask; 247 } 248 } 249 250 // Only 'single' files have the stream attribute set 251 ok(attributes_first & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_first (0x%lx)\n", attributes_first); 252 ok(!(attributes2 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes2 (0x%lx)\n", attributes2); 253 ok(!(attributes3 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes3 (0x%lx)\n", attributes3); 254 ok(attributes_last & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_last (0x%lx)\n", attributes_last); 255 256 // Only attributes common on all are returned, so only the last has the readonly bit set! 257 ok(!(attributes_first & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes_first (0x%lx)\n", attributes_first); 258 ok(!(attributes2 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes2 (0x%lx)\n", attributes2); 259 ok(!(attributes3 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes3 (0x%lx)\n", attributes3); 260 ok(attributes_last & SFGAO_READONLY, "Expected SFGAO_READONLY on attributes_last (0x%lx)\n", attributes_last); 261 262 // The actual tests 263 { 264 // Just the first file 265 CComPtr<IDataObject> spDataObject; 266 hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items, &spDataObject); 267 ok_hr_ret(hr, S_OK); 268 269 DWORD dwAttributeMask = 0, dwAttributes = 123; 270 UINT cItems = 123; 271 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 272 ok_hr(hr, S_OK); 273 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_first, 1); 274 } 275 276 { 277 // First 2 files 278 CComPtr<IDataObject> spDataObject; 279 hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 2, items, &spDataObject); 280 ok_hr_ret(hr, S_OK); 281 282 DWORD dwAttributeMask = 0, dwAttributes = 123; 283 UINT cItems = 123; 284 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 285 ok_hr(hr, S_OK); 286 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes2, 2); 287 } 288 289 { 290 // All 3 files 291 CComPtr<IDataObject> spDataObject; 292 hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 3, items, &spDataObject); 293 ok_hr_ret(hr, S_OK); 294 295 DWORD dwAttributeMask = 0, dwAttributes = 123; 296 UINT cItems = 123; 297 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 298 ok_hr(hr, S_OK); 299 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes3, 3); 300 } 301 302 { 303 // Only the last file 304 CComPtr<IDataObject> spDataObject; 305 hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items + 2, &spDataObject); 306 ok_hr_ret(hr, S_OK); 307 308 DWORD dwAttributeMask = 0, dwAttributes = 123; 309 UINT cItems = 123; 310 hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems); 311 ok_hr(hr, S_OK); 312 ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_last, 1); 313 } 314} 315 316START_TEST(SHGetAttributesFromDataObject) 317{ 318 HRESULT hr; 319 320 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 321 ok_hr(hr, S_OK); 322 if (!SUCCEEDED(hr)) 323 return; 324 325 g_DataObjectAttributes = (CLIPFORMAT)RegisterClipboardFormatW(L"DataObjectAttributes"); 326 ok(g_DataObjectAttributes != 0, "Unable to register DataObjectAttributes\n"); 327 328 test_SpecialCases(); 329 test_AttributesRegistration(); 330 test_MultipleFiles(); 331 332 CoUninitialize(); 333}