ReactOS  0.4.15-dev-5455-g015cd25
SHGetAttributesFromDataObject.cpp
Go to the documentation of this file.
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 
15 static CLIPFORMAT g_DataObjectAttributes = 0;
16 static const DWORD dwDefaultAttributeMask = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE | SFGAO_CANRENAME | SFGAO_CANDELETE |
17  SFGAO_READONLY | SFGAO_STREAM | SFGAO_FOLDER;
18 static_assert(dwDefaultAttributeMask == 0x2044003B, "Unexpected default attribute mask");
19 
20 
21 struct TmpFile
22 {
24 
25  void Create(LPCWSTR Folder)
26  {
27  GetTempFileNameW(Folder, L"SHG", 0, Buffer);
28  }
29 
31  {
32  if (Buffer[0])
33  {
36  }
37  }
38 };
39 
40 
41 CComPtr<IShellFolder> _BindToObject(PCUIDLIST_ABSOLUTE pidl)
42 {
43  CComPtr<IShellFolder> spDesktop, spResult;
44  HRESULT hr = SHGetDesktopFolder(&spDesktop);
46  return spResult;
47 
48  if (FAILED_UNEXPECTEDLY(spDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &spResult))))
49  {
50  spResult.Release();
51  }
52  return spResult;
53 }
54 
55 
56 static void ok_attributes_(IDataObject* pDataObject, HRESULT expect_hr, DWORD expect_mask, DWORD expect_attr, UINT expect_items)
57 {
58  FORMATETC fmt = { g_DataObjectAttributes, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
59  STGMEDIUM medium = {};
60 
61  HRESULT hr = pDataObject->GetData(&fmt, &medium);
62  winetest_ok(hr == expect_hr, "Unexpected result from GetData, got 0x%lx, expected 0x%lx\n", hr, expect_hr);
63 
64  if (hr == expect_hr && expect_hr == S_OK)
65  {
66  LPVOID blob = GlobalLock(medium.hGlobal);
67  winetest_ok(blob != nullptr, "Failed to lock hGlobal\n");
68  if (blob)
69  {
70  SIZE_T size = GlobalSize(medium.hGlobal);
71  winetest_ok(size == 0xc, "Unexpected size, got %lu, expected 12\n", size);
72  if (size == 0xc)
73  {
75  winetest_ok(data[0] == expect_mask, "Unexpected mask, got 0x%lx, expected 0x%lx\n", data[0], expect_mask);
76  winetest_ok(data[1] == expect_attr, "Unexpected attr, got 0x%lx, expected 0x%lx\n", data[1], expect_attr);
77  winetest_ok(data[2] == expect_items, "Unexpected item count, got %lu, expected %u\n", data[2], expect_items);
78  }
79  GlobalUnlock(medium.hGlobal);
80  }
81  }
82 
83  if (SUCCEEDED(hr))
84  ReleaseStgMedium(&medium);
85 }
86 
87 
88 #define ok_attributes (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_attributes_
89 #define ok_hr_ret(x, y) ok_hr(x, y); if (x != y) return
90 
91 static void test_SpecialCases()
92 {
93  DWORD dwAttributeMask = 0, dwAttributes = 123;
94  UINT cItems = 123;
95 
96  HRESULT hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, &cItems);
97  ok_hr(hr, S_OK);
98  ok_int(dwAttributes, 0);
99  ok_int(cItems, 0);
100 
101  cItems = 123;
102  hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, nullptr, &cItems);
103  ok_hr(hr, S_OK);
104  ok_int(cItems, 0);
105 
106  dwAttributes = 123;
107  hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, nullptr);
108  ok_hr(hr, S_OK);
109  ok_int(dwAttributes, 0);
110 }
111 
112 
114 {
115  WCHAR Buffer[MAX_PATH] = {};
116 
118  CComHeapPtr<ITEMIDLIST_ABSOLUTE> spPath(ILCreateFromPathW(Buffer));
119 
120  ok(spPath != nullptr, "Unable to create pidl from %S\n", Buffer);
121  if (spPath == nullptr)
122  return;
123 
124  SFGAOF attributes = dwDefaultAttributeMask;
125  HRESULT hr;
126  {
127  CComPtr<IShellFolder> spFolder;
129  hr = SHBindToParent(spPath, IID_PPV_ARG(IShellFolder, &spFolder), &child);
130  ok_hr_ret(hr, S_OK);
131 
132  hr = spFolder->GetAttributesOf(1, &child, &attributes);
133  ok_hr_ret(hr, S_OK);
134 
135  attributes &= dwDefaultAttributeMask;
136  }
137 
138  CComHeapPtr<ITEMIDLIST> parent(ILClone(spPath));
141 
142  CComPtr<IDataObject> spDataObject;
143  hr = CIDLData_CreateFromIDArray(parent, 1, &child, &spDataObject);
144  ok_hr_ret(hr, S_OK);
145 
146  /* Not registered yet */
147  ok_attributes(spDataObject, DV_E_FORMATETC, 0, 0, 0);
148 
149  /* Ask for attributes, without specifying any */
150  DWORD dwAttributeMask = 0, dwAttributes = 0;
151  UINT cItems = 0;
152  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
153  ok_hr(hr, S_OK);
154 
155  /* Now there are attributes registered */
156  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes, 1);
157 
158  // Now add an additional mask value (our exe should have a propsheet!)
159  dwAttributeMask = SFGAO_HASPROPSHEET;
160  dwAttributes = 0;
161  cItems = 0;
162  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
163  ok_hr(hr, S_OK);
164 
165  // Observe that this is now also cached
166  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask | SFGAO_HASPROPSHEET, attributes | SFGAO_HASPROPSHEET, 1);
167 }
168 
169 static void test_MultipleFiles()
170 {
171  TmpFile TmpFile1, TmpFile2, TmpFile3;
172 
173  CComHeapPtr<ITEMIDLIST> pidl_tmpfolder;
174  CComHeapPtr<ITEMIDLIST> pidl1, pidl2, pidl3;
175 
176  ITEMIDLIST* items[3] = {};
177  SFGAOF attributes_first = dwDefaultAttributeMask;
178  SFGAOF attributes2 = dwDefaultAttributeMask;
179  SFGAOF attributes3 = dwDefaultAttributeMask;
180  SFGAOF attributes_last = dwDefaultAttributeMask;
181 
182  HRESULT hr;
183  {
184  WCHAR TempFolder[MAX_PATH] = {};
185  GetTempPathW(_countof(TempFolder), TempFolder);
186 
187  // Create temp files
188  TmpFile1.Create(TempFolder);
189  TmpFile2.Create(TempFolder);
190  TmpFile3.Create(TempFolder);
191 
192  // Last file is read-only
194 
195  hr = SHParseDisplayName(TempFolder, NULL, &pidl_tmpfolder, NULL, NULL);
196  ok_hr_ret(hr, S_OK);
197 
198  CComPtr<IShellFolder> spFolder = _BindToObject(pidl_tmpfolder);
199  ok(!!spFolder, "Unable to bind to tmp folder\n");
200  if (!spFolder)
201  return;
202 
203  hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile1.Buffer), NULL, &pidl1, NULL);
204  ok_hr_ret(hr, S_OK);
205 
206  hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile2.Buffer), NULL, &pidl2, NULL);
207  ok_hr_ret(hr, S_OK);
208 
209  hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile3.Buffer), NULL, &pidl3, NULL);
210  ok_hr_ret(hr, S_OK);
211 
212  items[0] = pidl1;
213  items[1] = pidl2;
214  items[2] = pidl3;
215 
216  // Query file attributes
217  hr = spFolder->GetAttributesOf(1, items, &attributes_first);
218  ok_hr(hr, S_OK);
219 
220  hr = spFolder->GetAttributesOf(2, items, &attributes2);
221  ok_hr(hr, S_OK);
222 
223  hr = spFolder->GetAttributesOf(3, items, &attributes3);
224  ok_hr(hr, S_OK);
225 
226  hr = spFolder->GetAttributesOf(1, items + 2, &attributes_last);
227  ok_hr(hr, S_OK);
228 
229  // Ignore any non-default attributes
230  attributes_first &= dwDefaultAttributeMask;
231  attributes2 &= dwDefaultAttributeMask;
232  attributes3 &= dwDefaultAttributeMask;
233  attributes_last &= dwDefaultAttributeMask;
234  }
235 
236  // Only 'single' files have the stream attribute set
237  ok(attributes_first & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_first (0x%lx)\n", attributes_first);
238  ok(!(attributes2 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes2 (0x%lx)\n", attributes2);
239  ok(!(attributes3 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes3 (0x%lx)\n", attributes3);
240  ok(attributes_last & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_last (0x%lx)\n", attributes_last);
241 
242  // Only attributes common on all are returned, so only the last has the readonly bit set!
243  ok(!(attributes_first & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes_first (0x%lx)\n", attributes_first);
244  ok(!(attributes2 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes2 (0x%lx)\n", attributes2);
245  ok(!(attributes3 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes3 (0x%lx)\n", attributes3);
246  ok(attributes_last & SFGAO_READONLY, "Expected SFGAO_READONLY on attributes_last (0x%lx)\n", attributes_last);
247 
248  // The actual tests
249  {
250  // Just the first file
251  CComPtr<IDataObject> spDataObject;
252  hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items, &spDataObject);
253  ok_hr_ret(hr, S_OK);
254 
255  DWORD dwAttributeMask = 0, dwAttributes = 123;
256  UINT cItems = 123;
257  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
258  ok_hr(hr, S_OK);
259  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_first, 1);
260  }
261 
262  {
263  // First 2 files
264  CComPtr<IDataObject> spDataObject;
265  hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 2, items, &spDataObject);
266  ok_hr_ret(hr, S_OK);
267 
268  DWORD dwAttributeMask = 0, dwAttributes = 123;
269  UINT cItems = 123;
270  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
271  ok_hr(hr, S_OK);
272  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes2, 2);
273  }
274 
275  {
276  // All 3 files
277  CComPtr<IDataObject> spDataObject;
278  hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 3, items, &spDataObject);
279  ok_hr_ret(hr, S_OK);
280 
281  DWORD dwAttributeMask = 0, dwAttributes = 123;
282  UINT cItems = 123;
283  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
284  ok_hr(hr, S_OK);
285  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes3, 3);
286  }
287 
288  {
289  // Only the last file
290  CComPtr<IDataObject> spDataObject;
291  hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items + 2, &spDataObject);
292  ok_hr_ret(hr, S_OK);
293 
294  DWORD dwAttributeMask = 0, dwAttributes = 123;
295  UINT cItems = 123;
296  hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
297  ok_hr(hr, S_OK);
298  ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_last, 1);
299  }
300 }
301 
303 {
304  HRESULT hr;
305 
307  ok_hr(hr, S_OK);
308  if (!SUCCEEDED(hr))
309  return;
310 
311  g_DataObjectAttributes = (CLIPFORMAT)RegisterClipboardFormatW(L"DataObjectAttributes");
312  ok(g_DataObjectAttributes != 0, "Unable to register DataObjectAttributes\n");
313 
317 
318  CoUninitialize();
319 }
#define ok_hr(status, expected)
Definition: ACListISF.cpp:31
BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
Definition: fileinfo.c:794
START_TEST(SHGetAttributesFromDataObject)
DWORD WINAPI GetModuleFileNameW(HINSTANCE hModule, LPWSTR lpFilename, DWORD nSize)
Definition: loader.c:600
void Create(LPCWSTR Folder)
HRESULT hr
Definition: shlfolder.c:183
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185
Definition: image.c:133
void WINAPI ReleaseStgMedium(STGMEDIUM *pmedium)
Definition: ole2.c:2033
UINT WINAPI RegisterClipboardFormatW(_In_ LPCWSTR)
static const DWORD dwDefaultAttributeMask
const ITEMID_CHILD UNALIGNED * PCUITEMID_CHILD
Definition: shtypes.idl:70
HRESULT GetData([in, unique] FORMATETC *pformatetcIn, [out] STGMEDIUM *pmedium)
BOOL WINAPI ILRemoveLastID(LPITEMIDLIST pidl)
Definition: pidl.c:212
WCHAR Buffer[MAX_PATH]
#define IID_PPV_ARG(Itype, ppType)
static HWND child
Definition: cursoricon.c:298
DWORD dwAttributes
Definition: vdmdbg.h:34
SIZE_T NTAPI GlobalSize(HGLOBAL hMem)
Definition: heapmem.c:1090
#define L(x)
Definition: ntvdm.h:50
DWORD WINAPI GetTempPathW(IN DWORD count, OUT LPWSTR path)
Definition: path.c:2080
if SUCCEEDED(hr)
BOOL WINAPI DeleteFileW(IN LPCWSTR lpFileName)
Definition: delete.c:39
#define ok_int(expression, result)
Definition: atltest.h:134
LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath)
Definition: path.c:394
LPITEMIDLIST WINAPI ILFindLastID(LPCITEMIDLIST pidl)
Definition: pidl.c:189
Definition: bufpool.h:45
EXTERN_C HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject *pDataObject, DWORD dwAttributeMask, DWORD *pdwAttributes, UINT *pcItems)
HRESULT WINAPI SHGetDesktopFolder(IShellFolder **psf)
GLsizeiptr size
Definition: glext.h:5919
#define DV_E_FORMATETC
Definition: winerror.h:2633
#define ok_hr_ret(x, y)
r parent
Definition: btrfs.c:3010
__wchar_t WCHAR
Definition: xmlstorage.h:180
void __winetest_cdecl winetest_ok(int condition, const char *msg,...)
LONG HRESULT
Definition: typedefs.h:79
#define _countof(array)
Definition: sndvol32.h:68
#define FAILED_UNEXPECTEDLY(hr)
Definition: shellutils.h:82
#define MAX_PATH
Definition: compat.h:34
UINT WINAPI GetTempFileNameW(IN LPCWSTR lpPathName, IN LPCWSTR lpPrefixString, IN UINT uUnique, OUT LPWSTR lpTempFileName)
Definition: filename.c:84
#define FILE_ATTRIBUTE_READONLY
Definition: nt_native.h:702
unsigned long DWORD
Definition: ntddk_ex.h:95
static void test_MultipleFiles()
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
#define FILE_ATTRIBUTE_NORMAL
Definition: compat.h:137
LPVOID NTAPI GlobalLock(HGLOBAL hMem)
Definition: heapmem.c:755
#define expect_hr(expected, got)
Definition: assoc.c:27
ULONG_PTR SIZE_T
Definition: typedefs.h:80
#define S_OK
Definition: intsafe.h:52
const ITEMIDLIST_ABSOLUTE UNALIGNED * PCUIDLIST_ABSOLUTE
Definition: shtypes.idl:63
HRESULT WINAPI SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppv, LPCITEMIDLIST *ppidlLast)
Definition: pidl.c:1337
void WINAPI DECLSPEC_HOTPATCH CoUninitialize(void)
Definition: compobj.c:2067
LPITEMIDLIST WINAPI ILClone(LPCITEMIDLIST pidl)
Definition: pidl.c:228
#define ok(value,...)
Definition: atltest.h:57
HRESULT WINAPI SHParseDisplayName(LPCWSTR pszName, IBindCtx *pbc, LPITEMIDLIST *ppidl, SFGAOF sfgaoIn, SFGAOF *psfgaoOut)
Definition: pidl.c:1381
HRESULT WINAPI DECLSPEC_HOTPATCH CoInitializeEx(LPVOID lpReserved, DWORD dwCoInit)
Definition: compobj.c:2002
unsigned int UINT
Definition: ndis.h:50
#define NULL
Definition: types.h:112
BOOL NTAPI GlobalUnlock(HGLOBAL hMem)
Definition: heapmem.c:1190
DWORD * PDWORD
Definition: pedump.c:68
CComPtr< IShellFolder > _BindToObject(PCUIDLIST_ABSOLUTE pidl)
static void ok_attributes_(IDataObject *pDataObject, HRESULT expect_hr, DWORD expect_mask, DWORD expect_attr, UINT expect_items)
static CLIPFORMAT g_DataObjectAttributes
#define ok_attributes
HRESULT WINAPI CIDLData_CreateFromIDArray(PCIDLIST_ABSOLUTE pidlFolder, UINT cpidlFiles, PCUIDLIST_RELATIVE_ARRAY lppidlFiles, LPDATAOBJECT *ppdataObject)
Definition: shellord.c:2173
LPITEMIDLIST WINAPI ILCreateFromPathW(LPCWSTR path)
Definition: pidl.c:982
static TCHAR * items[]
Definition: page1.c:45
static void test_AttributesRegistration()
Definition: dsound.c:943
static void test_SpecialCases()
static IShellFolder IShellItem **static IBindCtx LPITEMIDLIST SFGAOF
Definition: ebrowser.c:83