Reactos
at master 246 lines 8.2 kB view raw
1/* 2 * PROJECT: shell32 3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) 4 * PURPOSE: SHOpenPropSheetW implementation 5 * COPYRIGHT: Copyright 2024 Whindmar Saksit <whindsaks@proton.me> 6 */ 7 8#include "precomp.h" 9 10WINE_DEFAULT_DEBUG_CHANNEL(shell); 11 12HRESULT 13SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID *pClsId) 14{ 15 // First try the key name 16 if (SUCCEEDED(SHCLSIDFromStringW(KeyName, pClsId))) 17 return S_OK; 18 WCHAR buf[42]; 19 DWORD cb = sizeof(buf); 20 // and then the default value 21 DWORD err = RegGetValueW(hKey, KeyName, NULL, RRF_RT_REG_SZ, NULL, buf, &cb); 22 return !err ? SHCLSIDFromStringW(buf, pClsId) : HRESULT_FROM_WIN32(err); 23} 24 25static HRESULT 26SHELL_InitializeExtension(REFCLSID clsid, PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDO, HKEY hkeyProgID, REFIID riid, void **ppv) 27{ 28 *ppv = NULL; 29 IUnknown *pUnk; 30 HRESULT hr = SHExtCoCreateInstance(NULL, &clsid, NULL, riid, (void**)&pUnk); 31 if (SUCCEEDED(hr)) 32 { 33 CComPtr<IShellExtInit> Init; 34 hr = pUnk->QueryInterface(IID_PPV_ARG(IShellExtInit, &Init)); 35 if (SUCCEEDED(hr)) 36 hr = Init->Initialize(pidlFolder, pDO, hkeyProgID); 37 38 if (SUCCEEDED(hr)) 39 *ppv = (void*)pUnk; 40 else 41 pUnk->Release(); 42 } 43 return hr; 44} 45 46static HRESULT 47AddPropSheetHandlerPages(REFCLSID clsid, IDataObject *pDO, HKEY hkeyProgID, PROPSHEETHEADERW &psh) 48{ 49 CComPtr<IShellPropSheetExt> SheetExt; 50 HRESULT hr = SHELL_InitializeExtension(clsid, NULL, pDO, hkeyProgID, IID_PPV_ARG(IShellPropSheetExt, &SheetExt)); 51 if (SUCCEEDED(hr)) 52 { 53 UINT OldCount = psh.nPages; 54 hr = SheetExt->AddPages(AddPropSheetPageCallback, (LPARAM)&psh); 55 // The returned index is one-based (relative to this extension). 56 // See https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellpropsheetext-addpages 57 if (hr > 0) 58 { 59 hr += OldCount; 60 psh.nStartPage = hr - 1; 61 } 62 } 63 return hr; 64} 65 66static HRESULT 67SHELL_CreatePropSheetStubWindow(CStubWindow32 &stub, PCIDLIST_ABSOLUTE pidl, const POINT *pPt) 68{ 69 PWSTR Path; 70 if (!pidl || FAILED(SHGetNameFromIDList(pidl, SIGDN_DESKTOPABSOLUTEPARSING, &Path))) 71 Path = NULL; // If we can't get a path, we simply will not be able to reuse this window 72 73 HRESULT hr = stub.CreateStub(CStubWindow32::TYPE_PROPERTYSHEET, Path, pPt); 74 SHFree(Path); 75 UINT flags = SHGFI_ICON | SHGFI_ADDOVERLAYS; 76 SHFILEINFO sfi; 77 if (SUCCEEDED(hr) && SHGetFileInfoW((LPWSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | flags)) 78 stub.SendMessage(WM_SETICON, ICON_BIG, (LPARAM)sfi.hIcon); 79 return hr; 80} 81 82/************************************************************************* 83 * SHELL32_PropertySheet [INTERNAL] 84 * PropertySheetW with stub window. 85 */ 86static INT_PTR 87SHELL32_PropertySheet(LPPROPSHEETHEADERW ppsh, IDataObject *pDO) 88{ 89 CStubWindow32 stub; 90 POINT pt, *ppt = NULL; 91 if (pDO && SUCCEEDED(DataObject_GetOffset(pDO, &pt))) 92 ppt = &pt; 93 PIDLIST_ABSOLUTE pidl = SHELL_DataObject_ILCloneFullItem(pDO, 0); 94 HRESULT hr = SHELL_CreatePropSheetStubWindow(stub, pidl, ppt); 95 ILFree(pidl); 96 if (FAILED(hr)) 97 return hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) ? 0 : -1; 98 99 INT_PTR Result = -1; 100 ppsh->hwndParent = stub; 101 if (ppsh->nPages) 102 { 103 Result = PropertySheetW(ppsh); 104 } 105 else 106 { 107 WCHAR szFormat[128], szMessage[_countof(szFormat) + 42]; 108 LoadStringW(shell32_hInstance, IDS_CANTSHOWPROPERTIES, szFormat, _countof(szFormat)); 109 wsprintfW(szMessage, szFormat, ERROR_CAN_NOT_COMPLETE); 110 MessageBoxW(NULL, szMessage, NULL, MB_ICONERROR); 111 } 112 stub.DestroyWindow(); 113 return Result; 114} 115 116/************************************************************************* 117 * SHELL32_OpenPropSheet [INTERNAL] 118 * The real implementation of SHOpenPropSheetW. 119 */ 120static BOOL 121SHELL32_OpenPropSheet(LPCWSTR pszCaption, HKEY *ahKeys, UINT cKeys, 122 const CLSID *pclsidDefault, IDataObject *pDO, LPCWSTR pStartPage) 123{ 124 HKEY hKeyProgID = cKeys ? ahKeys[cKeys - 1] : NULL; // Windows uses the last key for some reason 125 HPROPSHEETPAGE hppages[MAX_PROPERTY_SHEET_PAGE]; 126 PROPSHEETHEADERW psh = { sizeof(psh), PSH_PROPTITLE }; 127 psh.phpage = hppages; 128 psh.hInstance = shell32_hInstance; 129 psh.pszCaption = pszCaption; 130 psh.pStartPage = pStartPage; 131 132 if (pclsidDefault) 133 AddPropSheetHandlerPages(*pclsidDefault, pDO, hKeyProgID, psh); 134 135 for (UINT i = 0; i < cKeys; ++i) 136 { 137 // Note: We can't use SHCreatePropSheetExtArrayEx because we need the AddPages() return value (see AddPropSheetHandlerPages). 138 HKEY hKey; 139 if (RegOpenKeyExW(ahKeys[i], L"shellex\\PropertySheetHandlers", 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) 140 continue; 141 for (UINT index = 0;; ++index) 142 { 143 WCHAR KeyName[MAX_PATH]; 144 LRESULT err = RegEnumKeyW(hKey, index, KeyName, _countof(KeyName)); 145 KeyName[_countof(KeyName)-1] = UNICODE_NULL; 146 if (err == ERROR_MORE_DATA) 147 continue; 148 if (err) 149 break; 150 CLSID clsid; 151 if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, &clsid))) 152 AddPropSheetHandlerPages(clsid, pDO, hKeyProgID, psh); 153 } 154 RegCloseKey(hKey); 155 } 156 157 if (pStartPage == psh.pStartPage && pStartPage) 158 psh.dwFlags |= PSH_USEPSTARTPAGE; 159 INT_PTR Result = SHELL32_PropertySheet(&psh, pDO); 160 return (Result != -1); 161} 162 163/************************************************************************* 164 * SHOpenPropSheetA [SHELL32.707] 165 * 166 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheeta 167 */ 168EXTERN_C 169BOOL WINAPI 170SHOpenPropSheetA( 171 _In_opt_ LPCSTR pszCaption, 172 _In_opt_ HKEY *ahKeys, 173 _In_ UINT cKeys, 174 _In_ const CLSID *pclsidDefault, 175 _In_ IDataObject *pDataObject, 176 _In_opt_ IShellBrowser *pShellBrowser, 177 _In_opt_ LPCSTR pszStartPage) 178{ 179 CStringW strStartPageW, strCaptionW; 180 LPCWSTR pszCaptionW = NULL, pszStartPageW = NULL; 181 182 if (pszCaption) 183 { 184 strStartPageW = pszCaption; 185 pszCaptionW = strCaptionW; 186 } 187 188 if (pszStartPage) 189 { 190 strStartPageW = pszStartPage; 191 pszStartPageW = strStartPageW; 192 } 193 194 return SHOpenPropSheetW(pszCaptionW, ahKeys, cKeys, pclsidDefault, 195 pDataObject, pShellBrowser, pszStartPageW); 196} 197 198/************************************************************************* 199 * SHOpenPropSheetW [SHELL32.80] 200 * 201 * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheetw 202 */ 203EXTERN_C BOOL WINAPI 204SHOpenPropSheetW( 205 _In_opt_ LPCWSTR pszCaption, 206 _In_opt_ HKEY *ahKeys, 207 _In_ UINT cKeys, 208 _In_ const CLSID *pclsidDefault, 209 _In_ IDataObject *pDataObject, 210 _In_opt_ IShellBrowser *pShellBrowser, 211 _In_opt_ LPCWSTR pszStartPage) 212{ 213 UNREFERENCED_PARAMETER(pShellBrowser); /* MSDN says "Not used". */ 214 return SHELL32_OpenPropSheet(pszCaption, ahKeys, cKeys, pclsidDefault, pDataObject, pszStartPage); 215} 216 217/************************************************************************* 218 * SH_CreatePropertySheetPage [Internal] 219 * 220 * creates a property sheet page from a resource id 221 */ 222HPROPSHEETPAGE 223SH_CreatePropertySheetPageEx(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, 224 LPCWSTR pwszTitle, LPFNPSPCALLBACK Callback) 225{ 226 PROPSHEETPAGEW Page = { sizeof(Page), PSP_DEFAULT, shell32_hInstance }; 227 Page.pszTemplate = MAKEINTRESOURCEW(wDialogId); 228 Page.pfnDlgProc = pfnDlgProc; 229 Page.lParam = lParam; 230 Page.pszTitle = pwszTitle; 231 Page.pfnCallback = Callback; 232 233 if (pwszTitle) 234 Page.dwFlags |= PSP_USETITLE; 235 236 if (Callback) 237 Page.dwFlags |= PSP_USECALLBACK; 238 239 return CreatePropertySheetPageW(&Page); 240} 241 242HPROPSHEETPAGE 243SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle) 244{ 245 return SH_CreatePropertySheetPageEx(wDialogId, pfnDlgProc, lParam, pwszTitle, NULL); 246}