Reactos
1/*
2 * PROJECT: shell32
3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4 * PURPOSE: FileSystem PropertySheet implementation
5 * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks@proton.me>
6 */
7
8#include "precomp.h"
9
10WINE_DEFAULT_DEBUG_CHANNEL(shell);
11
12static HRESULT
13SHELL_GetCaptionFromDataObject(IDataObject *pDO, LPWSTR Buf, UINT cchBuf)
14{
15 HRESULT hr = E_INVALIDARG;
16 if (PIDLIST_ABSOLUTE pidl = SHELL_DataObject_ILCloneFullItem(pDO, 0))
17 {
18 hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, Buf, cchBuf, NULL);
19 ILFree(pidl);
20
21 if (SUCCEEDED(hr) && DataObject_GetHIDACount(pDO) > 1)
22 StringCchCatW(Buf, cchBuf, L", ...");
23 }
24 return hr;
25}
26
27struct ShellPropSheetDialog
28{
29 typedef void (CALLBACK*PFNINITIALIZE)(LPCWSTR InitString, IDataObject *pDO,
30 HKEY *hKeys, UINT *cKeys);
31
32 static HRESULT Show(const CLSID *pClsidDefault, IDataObject *pDO,
33 PFNINITIALIZE InitFunc, LPCWSTR InitString)
34 {
35 HRESULT hr;
36 CRegKeyHandleArray keys;
37 if (InitFunc)
38 InitFunc(InitString, pDO, keys, keys);
39 WCHAR szCaption[MAX_PATH], *pszCaption = NULL;
40 if (SUCCEEDED(SHELL_GetCaptionFromDataObject(pDO, szCaption, _countof(szCaption))))
41 pszCaption = szCaption;
42 hr = SHOpenPropSheetW(pszCaption, keys, keys, pClsidDefault, pDO, NULL, NULL) ? S_OK : E_FAIL;
43 return hr;
44 }
45
46 struct DATA
47 {
48 PFNINITIALIZE InitFunc;
49 LPWSTR InitString;
50 CLSID ClsidDefault;
51 const CLSID *pClsidDefault;
52 IStream *pObjStream;
53 HANDLE hEvent;
54 };
55
56 static void FreeData(DATA *pData)
57 {
58 if (pData->InitString)
59 SHFree(pData->InitString);
60 SHFree(pData);
61 }
62
63 static HRESULT ShowAsync(const CLSID *pClsidDefault, IDataObject *pDO,
64 PFNINITIALIZE InitFunc, LPCWSTR InitString)
65 {
66 DATA *pData = (DATA*)SHAlloc(sizeof(*pData));
67 if (!pData)
68 return E_OUTOFMEMORY;
69 ZeroMemory(pData, sizeof(*pData));
70 pData->InitFunc = InitFunc;
71 if (InitString && FAILED(SHStrDupW(InitString, &pData->InitString)))
72 {
73 FreeData(pData);
74 return E_OUTOFMEMORY;
75 }
76 if (pClsidDefault)
77 {
78 pData->ClsidDefault = *pClsidDefault;
79 pData->pClsidDefault = &pData->ClsidDefault;
80 }
81 HANDLE hEvent = pData->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
82
83 HRESULT hr = S_OK;
84 if (pDO)
85 hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDO, &pData->pObjStream);
86
87 const UINT flags = CTF_COINIT | CTF_PROCESS_REF | CTF_INSIST;
88 if (SUCCEEDED(hr) && !SHCreateThread(ShowPropertiesThread, pData, flags, NULL))
89 {
90 if (pData->pObjStream)
91 pData->pObjStream->Release();
92 hr = E_FAIL;
93 }
94
95 if (SUCCEEDED(hr))
96 {
97 if (hEvent)
98 {
99 DWORD index;
100 // Pump COM messages until the thread can create its own IDataObject (for CORE-19933).
101 // SHOpenPropSheetW is modal and we cannot wait for it to complete.
102 CoWaitForMultipleHandles(COWAIT_DEFAULT, INFINITE, 1, &hEvent, &index);
103 CloseHandle(hEvent);
104 }
105 }
106 else
107 {
108 FreeData(pData);
109 }
110 return hr;
111 }
112
113 static DWORD CALLBACK ShowPropertiesThread(LPVOID Param)
114 {
115 DATA *pData = (DATA*)Param;
116 CComPtr<IDataObject> pDO, pLocalDO;
117 if (pData->pObjStream)
118 CoGetInterfaceAndReleaseStream(pData->pObjStream, IID_PPV_ARG(IDataObject, &pDO));
119 if (pDO && SUCCEEDED(SHELL_CloneDataObject(pDO, &pLocalDO)))
120 pDO = pLocalDO;
121 if (pData->hEvent)
122 SetEvent(pData->hEvent);
123 Show(pData->pClsidDefault, pDO, pData->InitFunc, pData->InitString);
124 FreeData(pData);
125 return 0;
126 }
127};
128
129static void CALLBACK
130FSFolderItemPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys)
131{
132 UNREFERENCED_PARAMETER(InitString);
133 CDataObjectHIDA cida(pDO);
134 if (SUCCEEDED(cida.hr()) && cida->cidl)
135 {
136#if 0
137 CCidaChildArrayHelper items(cida);
138 if (FAILED(hr = items.hr()))
139 return hr;
140#else
141 // Note: Since we are only passing a single item to AddFSClassKeysToArray,
142 // we don't need the rest of the array to be valid.
143 PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0);
144#endif
145 AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages
146 }
147}
148
149static void CALLBACK
150ClassPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys)
151{
152 UNREFERENCED_PARAMETER(pDO);
153 AddClassKeyToArray(InitString, hKeys, cKeys); // Add pages from HKCR\%ProgId% (with shellex\PropertySheetHandlers appended later)
154}
155
156HRESULT
157SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
158{
159 CDataObjectHIDA cida(pDO);
160 HRESULT hr = cida.hr();
161 if (FAILED_UNEXPECTEDLY(hr))
162 return hr;
163
164 const CLSID *pClsid = NULL;
165 ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL;
166 LPCWSTR InitString = NULL;
167
168 if (cida->cidl && _ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
169 {
170 pClsid = &CLSID_ShellDrvDefExt;
171 InitFunc = ClassPropDialogInitCallback;
172 InitString = L"Drive";
173 }
174 else
175 {
176 pClsid = &CLSID_ShellFileDefExt;
177 InitFunc = FSFolderItemPropDialogInitCallback;
178 }
179 return ShellPropSheetDialog().ShowAsync(pClsid, pDO, InitFunc, InitString);
180}
181
182HRESULT
183SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO)
184{
185 if (DataObject_GetHIDACount(pDO) == 1)
186 return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDO);
187 return SHMultiFileProperties(pDO, 0);
188}
189
190HRESULT
191SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
192{
193 WCHAR ClassBuf[6 + 38 + 1] = L"CLSID\\";
194 StringFromGUID2(*pClsid, ClassBuf + 6, 38 + 1);
195 return ShellPropSheetDialog().ShowAsync(NULL, pDO, ClassPropDialogInitCallback, ClassBuf);
196}
197
198HRESULT
199SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl)
200{
201 assert(pidl);
202
203 CComHeapPtr<ITEMIDLIST> alloc;
204 if (IS_INTRESOURCE(pidl))
205 {
206 HRESULT hr = SHGetSpecialFolderLocation(NULL, LOWORD(pidl), const_cast<LPITEMIDLIST*>(&pidl));
207 if (FAILED(hr))
208 return hr;
209 alloc.Attach(const_cast<LPITEMIDLIST>(pidl));
210 }
211 SHELLEXECUTEINFOA sei = {
212 sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK, NULL, "properties",
213 NULL, NULL, NULL, SW_SHOWNORMAL, NULL, const_cast<LPITEMIDLIST>(pidl)
214 };
215 return ShellExecuteExA(&sei) ? S_OK : HResultFromWin32(GetLastError());
216}
217
218/*
219 * SHMultiFileProperties [SHELL32.716]
220 */
221EXTERN_C HRESULT
222WINAPI
223SHMultiFileProperties(IDataObject *pDataObject, DWORD dwFlags)
224{
225 if (DataObject_GetHIDACount(pDataObject) == 1)
226 return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDataObject);
227
228 return ShellPropSheetDialog().ShowAsync(&CLSID_ShellFileDefExt, pDataObject, NULL, NULL);
229}