Reactos
at master 229 lines 7.3 kB view raw
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}