1 /*
2 * PROJECT: shell32
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: SHGetAttributesFromDataObject implementation
5 * COPYRIGHT: Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
6 */
7
8
9 #include "precomp.h"
10
11 WINE_DEFAULT_DEBUG_CHANNEL(shell);
12
13
14 static CLIPFORMAT g_DataObjectAttributes = 0;
15 static const DWORD dwDefaultAttributeMask = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE | SFGAO_CANRENAME |
16 SFGAO_CANDELETE | SFGAO_READONLY | SFGAO_STREAM | SFGAO_FOLDER;
17
18 struct DataObjectAttributes
19 {
20 DWORD dwMask;
21 DWORD dwAttributes;
22 UINT cItems;
23 };
24
25 static_assert(sizeof(DataObjectAttributes) == 0xc, "Unexpected struct size!");
26
27
28 static
_BindToObject(PCUIDLIST_ABSOLUTE pidl,CComPtr<IShellFolder> & spFolder)29 HRESULT _BindToObject(PCUIDLIST_ABSOLUTE pidl, CComPtr<IShellFolder>& spFolder)
30 {
31 return SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellFolder, &spFolder));
32 }
33
34 EXTERN_C
SHGetAttributesFromDataObject(IDataObject * pDataObject,DWORD dwAttributeMask,DWORD * pdwAttributes,UINT * pcItems)35 HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject* pDataObject, DWORD dwAttributeMask, DWORD* pdwAttributes, UINT* pcItems)
36 {
37 DWORD dwAttributes = 0;
38 DWORD cItems = 0;
39 HRESULT hr = S_OK;
40
41 TRACE("(%p, 0x%x, %p, %p)\n", pDataObject, dwAttributeMask, pdwAttributes, pcItems);
42
43 if (!g_DataObjectAttributes)
44 g_DataObjectAttributes = (CLIPFORMAT)RegisterClipboardFormatW(L"DataObjectAttributes");
45
46 if (pDataObject)
47 {
48 DataObjectAttributes data = {};
49 if (FAILED(DataObject_GetData(pDataObject, g_DataObjectAttributes, &data, sizeof(data))))
50 {
51 TRACE("No attributes yet, creating new\n");
52 memset(&data, 0, sizeof(data));
53 }
54
55 DWORD dwQueryAttributes = dwAttributeMask | dwDefaultAttributeMask;
56
57 if ((data.dwMask & dwQueryAttributes) != dwQueryAttributes)
58 {
59 CDataObjectHIDA hida(pDataObject);
60 CComPtr<IShellFolder> spFolder;
61
62 if (!FAILED_UNEXPECTEDLY(hr = hida.hr()) &&
63 !FAILED_UNEXPECTEDLY(hr = _BindToObject(HIDA_GetPIDLFolder(hida), spFolder)))
64 {
65 CSimpleArray<PCUIDLIST_RELATIVE> apidl;
66 for (UINT n = 0; n < hida->cidl; ++n)
67 {
68 apidl.Add(HIDA_GetPIDLItem(hida, n));
69 }
70
71 SFGAOF rgfInOut = dwQueryAttributes;
72 hr = spFolder->GetAttributesOf(apidl.GetSize(), apidl.GetData(), &rgfInOut);
73 if (!FAILED_UNEXPECTEDLY(hr))
74 {
75 data.dwMask = dwQueryAttributes;
76 // Only store what we asked for
77 data.dwAttributes = rgfInOut & dwQueryAttributes;
78 data.cItems = apidl.GetSize();
79
80 HRESULT hr2;
81 hr2 = DataObject_SetData(pDataObject, g_DataObjectAttributes, &data, sizeof(data));
82 FAILED_UNEXPECTEDLY(hr2); // Report cache failure but don't fail the function
83 }
84 }
85 }
86
87 // Only give the user what they asked for, not everything else we have!
88 dwAttributes = data.dwAttributes & dwAttributeMask;
89 cItems = data.cItems;
90 }
91
92 if (pdwAttributes)
93 *pdwAttributes = dwAttributes;
94
95 if (pcItems)
96 *pcItems = cItems;
97
98 return hr;
99 }
100
SHELL_CIDA_ILCloneFull(_In_ const CIDA * pCIDA,_In_ UINT Index)101 PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index)
102 {
103 if (Index < pCIDA->cidl)
104 return ILCombine(HIDA_GetPIDLFolder(pCIDA), HIDA_GetPIDLItem(pCIDA, Index));
105 return NULL;
106 }
107
SHELL_DataObject_ILCloneFullItem(_In_ IDataObject * pDO,_In_ UINT Index)108 PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index)
109 {
110 if (!pDO)
111 return NULL;
112 CDataObjectHIDA cida(pDO);
113 return SUCCEEDED(cida.hr()) ? SHELL_CIDA_ILCloneFull(cida, Index) : NULL;
114 }
115
SHELL_CloneDataObject(_In_ IDataObject * pDO,_Out_ IDataObject ** ppDO)116 HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO)
117 {
118 *ppDO = NULL;
119 CDataObjectHIDA cida(pDO);
120 HRESULT hr = cida.hr();
121 if (SUCCEEDED(hr))
122 {
123 PCUITEMID_CHILD items = HIDA_GetPIDLItem(cida, 0);
124 hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, &items, NULL, ppDO);
125 if (SUCCEEDED(hr))
126 {
127 POINT pt;
128 if (SUCCEEDED(DataObject_GetOffset(pDO, &pt)))
129 DataObject_SetOffset(*ppDO, &pt);
130 }
131 }
132 return hr;
133 }
134