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
10 WINE_DEFAULT_DEBUG_CHANNEL(shell);
11
12 static HRESULT
SHELL_GetCaptionFromDataObject(IDataObject * pDO,LPWSTR Buf,UINT cchBuf)13 SHELL_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 return hr;
22 }
23
24 struct ShellPropSheetDialog
25 {
26 typedef void (CALLBACK*PFNINITIALIZE)(LPCWSTR InitString, IDataObject *pDO,
27 HKEY *hKeys, UINT *cKeys);
28
ShowShellPropSheetDialog29 static HRESULT Show(const CLSID *pClsidDefault, IDataObject *pDO,
30 PFNINITIALIZE InitFunc, LPCWSTR InitString)
31 {
32 HRESULT hr;
33 CRegKeyHandleArray keys;
34 if (InitFunc)
35 InitFunc(InitString, pDO, keys, keys);
36 WCHAR szCaption[MAX_PATH], *pszCaption = NULL;
37 if (SUCCEEDED(SHELL_GetCaptionFromDataObject(pDO, szCaption, _countof(szCaption))))
38 pszCaption = szCaption;
39 hr = SHOpenPropSheetW(pszCaption, keys, keys, pClsidDefault, pDO, NULL, NULL) ? S_OK : E_FAIL;
40 return hr;
41 }
42
43 struct DATA
44 {
45 PFNINITIALIZE InitFunc;
46 LPWSTR InitString;
47 CLSID ClsidDefault;
48 const CLSID *pClsidDefault;
49 IStream *pObjStream;
50 HANDLE hEvent;
51 };
52
FreeDataShellPropSheetDialog53 static void FreeData(DATA *pData)
54 {
55 if (pData->InitString)
56 SHFree(pData->InitString);
57 SHFree(pData);
58 }
59
ShowAsyncShellPropSheetDialog60 static HRESULT ShowAsync(const CLSID *pClsidDefault, IDataObject *pDO,
61 PFNINITIALIZE InitFunc, LPCWSTR InitString)
62 {
63 DATA *pData = (DATA*)SHAlloc(sizeof(*pData));
64 if (!pData)
65 return E_OUTOFMEMORY;
66 ZeroMemory(pData, sizeof(*pData));
67 pData->InitFunc = InitFunc;
68 if (InitString && FAILED(SHStrDupW(InitString, &pData->InitString)))
69 {
70 FreeData(pData);
71 return E_OUTOFMEMORY;
72 }
73 if (pClsidDefault)
74 {
75 pData->ClsidDefault = *pClsidDefault;
76 pData->pClsidDefault = &pData->ClsidDefault;
77 }
78 HANDLE hEvent = pData->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
79
80 HRESULT hr = S_OK;
81 if (pDO)
82 hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDO, &pData->pObjStream);
83
84 const UINT flags = CTF_COINIT | CTF_PROCESS_REF | CTF_INSIST;
85 if (SUCCEEDED(hr) && !SHCreateThread(ShowPropertiesThread, pData, flags, NULL))
86 {
87 if (pData->pObjStream)
88 pData->pObjStream->Release();
89 hr = E_FAIL;
90 }
91
92 if (SUCCEEDED(hr))
93 {
94 if (hEvent)
95 {
96 DWORD index;
97 // Pump COM messages until the thread can create its own IDataObject (for CORE-19933).
98 // SHOpenPropSheetW is modal and we cannot wait for it to complete.
99 CoWaitForMultipleHandles(COWAIT_DEFAULT, INFINITE, 1, &hEvent, &index);
100 CloseHandle(hEvent);
101 }
102 }
103 else
104 {
105 FreeData(pData);
106 }
107 return hr;
108 }
109
ShowPropertiesThreadShellPropSheetDialog110 static DWORD CALLBACK ShowPropertiesThread(LPVOID Param)
111 {
112 DATA *pData = (DATA*)Param;
113 CComPtr<IDataObject> pDO, pLocalDO;
114 if (pData->pObjStream)
115 CoGetInterfaceAndReleaseStream(pData->pObjStream, IID_PPV_ARG(IDataObject, &pDO));
116 if (pDO && SUCCEEDED(SHELL_CloneDataObject(pDO, &pLocalDO)))
117 pDO = pLocalDO;
118 if (pData->hEvent)
119 SetEvent(pData->hEvent);
120 Show(pData->pClsidDefault, pDO, pData->InitFunc, pData->InitString);
121 FreeData(pData);
122 return 0;
123 }
124 };
125
126 static void CALLBACK
FSFolderItemPropDialogInitCallback(LPCWSTR InitString,IDataObject * pDO,HKEY * hKeys,UINT * cKeys)127 FSFolderItemPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys)
128 {
129 UNREFERENCED_PARAMETER(InitString);
130 CDataObjectHIDA cida(pDO);
131 if (SUCCEEDED(cida.hr()) && cida->cidl)
132 {
133 PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0);
134 AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages
135 }
136 }
137
138 static void CALLBACK
ClassPropDialogInitCallback(LPCWSTR InitString,IDataObject * pDO,HKEY * hKeys,UINT * cKeys)139 ClassPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys)
140 {
141 UNREFERENCED_PARAMETER(pDO);
142 AddClassKeyToArray(InitString, hKeys, cKeys); // Add pages from HKCR\%ProgId% (with shellex\PropertySheetHandlers appended later)
143 }
144
145 HRESULT
SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject * pDO)146 SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
147 {
148 CDataObjectHIDA cida(pDO);
149 HRESULT hr = cida.hr();
150 if (FAILED_UNEXPECTEDLY(hr))
151 return hr;
152
153 const CLSID *pClsid = NULL;
154 ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL;
155 LPCWSTR InitString = NULL;
156
157 if (_ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
158 {
159 pClsid = &CLSID_ShellDrvDefExt;
160 InitFunc = ClassPropDialogInitCallback;
161 InitString = L"Drive";
162 }
163 else
164 {
165 pClsid = &CLSID_ShellFileDefExt;
166 InitFunc = FSFolderItemPropDialogInitCallback;
167 }
168 ShellPropSheetDialog Dialog;
169 return Dialog.ShowAsync(pClsid, pDO, InitFunc, InitString);
170 }
171
172 HRESULT
SHELL32_ShowShellExtensionProperties(const CLSID * pClsid,IDataObject * pDO)173 SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
174 {
175 WCHAR ClassBuf[6 + 38 + 1] = L"CLSID\\";
176 StringFromGUID2(*pClsid, ClassBuf + 6, 38 + 1);
177 ShellPropSheetDialog Dialog;
178 return Dialog.ShowAsync(NULL, pDO, ClassPropDialogInitCallback, ClassBuf);
179 }
180
181 HRESULT
SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl)182 SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl)
183 {
184 assert(pidl);
185
186 CComHeapPtr<ITEMIDLIST> alloc;
187 if (IS_INTRESOURCE(pidl))
188 {
189 HRESULT hr = SHGetSpecialFolderLocation(NULL, LOWORD(pidl), const_cast<LPITEMIDLIST*>(&pidl));
190 if (FAILED(hr))
191 return hr;
192 alloc.Attach(const_cast<LPITEMIDLIST>(pidl));
193 }
194 SHELLEXECUTEINFOA sei = {
195 sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK, NULL, "properties",
196 NULL, NULL, NULL, SW_SHOWNORMAL, NULL, const_cast<LPITEMIDLIST>(pidl)
197 };
198 return ShellExecuteExA(&sei) ? S_OK : HResultFromWin32(GetLastError());
199 }
200