diff --git a/dll/win32/shell32/CFolderOptions.cpp b/dll/win32/shell32/CFolderOptions.cpp index ba436c938fc..0f3513bcd8c 100644 --- a/dll/win32/shell32/CFolderOptions.cpp +++ b/dll/win32/shell32/CFolderOptions.cpp @@ -46,14 +46,18 @@ HRESULT STDMETHODCALLTYPE CFolderOptions::AddPages(LPFNSVADDPROPSHEETPAGE pfnAdd HPROPSHEETPAGE hPage; LPARAM sheetparam = (LPARAM)static_cast(this); - hPage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, sheetparam, NULL); - if (hPage == NULL) + hPage = SH_CreatePropertySheetPageEx(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, + sheetparam, NULL, &PropSheetPageLifetimeCallback); + HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam); + if (FAILED_UNEXPECTEDLY(hr)) { ERR("Failed to create property sheet page FolderOptionsGeneral\n"); - return E_FAIL; + return hr; + } + else + { + AddRef(); // For PropSheetPageLifetimeCallback } - if (!pfnAddPage(hPage, lParam)) - return E_FAIL; hPage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_VIEW, FolderOptionsViewDlg, sheetparam, NULL); if (hPage == NULL) diff --git a/dll/win32/shell32/CMakeLists.txt b/dll/win32/shell32/CMakeLists.txt index d7f7672f581..258191318b6 100644 --- a/dll/win32/shell32/CMakeLists.txt +++ b/dll/win32/shell32/CMakeLists.txt @@ -31,7 +31,7 @@ list(APPEND SOURCE dialogs/filedefext.cpp dialogs/filetypes.cpp dialogs/folder_options.cpp - dialogs/fprop.cpp + dialogs/item_prop.cpp dialogs/general.cpp dialogs/recycler_prop.cpp dialogs/view.cpp @@ -40,6 +40,7 @@ list(APPEND SOURCE CExtractIcon.cpp folders.cpp iconcache.cpp + propsheet.cpp shell32.cpp utils.cpp CShellItem.cpp diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp index 582a605ba54..d99db2f1470 100644 --- a/dll/win32/shell32/CShellLink.cpp +++ b/dll/win32/shell32/CShellLink.cpp @@ -2903,10 +2903,13 @@ BOOL CShellLink::OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam) ASSERT(FAILED(hr) || !(path[0] == ':' && path[1] == ':' && path[2] == '{')); } } - EnableWindow(GetDlgItem(hwndDlg, IDC_SHORTCUT_TARGET_TEXT), !disablecontrols); + + HWND hWndTarget = GetDlgItem(hwndDlg, IDC_SHORTCUT_TARGET_TEXT); + EnableWindow(hWndTarget, !disablecontrols); + PostMessage(hWndTarget, EM_SETSEL, 0, -1); // Fix caret bug when first opening the tab /* auto-completion */ - SHAutoComplete(GetDlgItem(hwndDlg, IDC_SHORTCUT_TARGET_TEXT), SHACF_DEFAULT); + SHAutoComplete(hWndTarget, SHACF_DEFAULT); SHAutoComplete(GetDlgItem(hwndDlg, IDC_SHORTCUT_START_IN_EDIT), SHACF_DEFAULT); m_bInInit = FALSE; @@ -3095,17 +3098,15 @@ CShellLink::SH_ShellLinkDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l HRESULT STDMETHODCALLTYPE CShellLink::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) { - HPROPSHEETPAGE hPage = SH_CreatePropertySheetPage(IDD_SHORTCUT_PROPERTIES, SH_ShellLinkDlgProc, (LPARAM)this, NULL); - if (hPage == NULL) - { - ERR("failed to create property sheet page\n"); - return E_FAIL; - } - - if (!pfnAddPage(hPage, lParam)) - return E_FAIL; - - return S_OK; + HPROPSHEETPAGE hPage = SH_CreatePropertySheetPageEx(IDD_SHORTCUT_PROPERTIES, SH_ShellLinkDlgProc, + (LPARAM)this, NULL, &PropSheetPageLifetimeCallback); + HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + else + AddRef(); // For PropSheetPageLifetimeCallback + enum { CShellLink_PageIndex_Shortcut = 0 }; + return 1 + CShellLink_PageIndex_Shortcut; // Make this page the default (one-based) } HRESULT STDMETHODCALLTYPE CShellLink::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE pfnReplacePage, LPARAM lParam) diff --git a/dll/win32/shell32/dialogs/drive.cpp b/dll/win32/shell32/dialogs/drive.cpp index 8bade5d68c8..ff97054fcda 100644 --- a/dll/win32/shell32/dialogs/drive.cpp +++ b/dll/win32/shell32/dialogs/drive.cpp @@ -32,9 +32,6 @@ typedef struct BOOL bFormattingNow; } FORMAT_DRIVE_CONTEXT, *PFORMAT_DRIVE_CONTEXT; -EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj); -HPROPSHEETPAGE SH_CreatePropertySheetPage(LPCSTR resname, DLGPROC dlgproc, LPARAM lParam, LPWSTR szTitle); - /* * TODO: In Windows the Shell doesn't know by itself if a drive is * a system one or not but rather a packet message is being sent by @@ -159,125 +156,6 @@ GetDefaultClusterSize(LPWSTR szFs, PDWORD pClusterSize, PULARGE_INTEGER TotalNum return TRUE; } -typedef struct _DRIVE_PROP_PAGE -{ - LPCSTR resname; - DLGPROC dlgproc; - UINT DriveType; -} DRIVE_PROP_PAGE; - -struct DRIVE_PROP_DATA -{ - PWSTR pwszDrive; - IStream *pStream; -}; - -static DWORD WINAPI -ShowDrivePropThreadProc(LPVOID pParam) -{ - CHeapPtr pPropData((DRIVE_PROP_DATA *)pParam); - CHeapPtr pwszDrive(pPropData->pwszDrive); - - // Unmarshall IDataObject from IStream - CComPtr pDataObj; - CoGetInterfaceAndReleaseStream(pPropData->pStream, IID_PPV_ARG(IDataObject, &pDataObj)); - - HPSXA hpsx = NULL; - HPROPSHEETPAGE hpsp[MAX_PROPERTY_SHEET_PAGE]; - CComObject *pDrvDefExt = NULL; - - CDataObjectHIDA cida(pDataObj); - if (FAILED_UNEXPECTEDLY(cida.hr())) - return FAILED(cida.hr()); - - RECT rcPosition = {CW_USEDEFAULT, CW_USEDEFAULT, 0, 0}; - POINT pt; - if (SUCCEEDED(DataObject_GetOffset(pDataObj, &pt))) - { - rcPosition.left = pt.x; - rcPosition.top = pt.y; - } - - DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION; - DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; - CStubWindow32 stub; - if (!stub.Create(NULL, rcPosition, NULL, style, exstyle)) - { - ERR("StubWindow32 creation failed\n"); - return FALSE; - } - - PROPSHEETHEADERW psh = {sizeof(PROPSHEETHEADERW)}; - psh.dwFlags = PSH_PROPTITLE; - psh.pszCaption = pwszDrive; - psh.hwndParent = stub; - psh.nStartPage = 0; - psh.phpage = hpsp; - - HRESULT hr = CComObject::CreateInstance(&pDrvDefExt); - if (SUCCEEDED(hr)) - { - pDrvDefExt->AddRef(); // CreateInstance returns object with 0 ref count - hr = pDrvDefExt->Initialize(HIDA_GetPIDLFolder(cida), pDataObj, NULL); - if (SUCCEEDED(hr)) - { - hr = pDrvDefExt->AddPages(AddPropSheetPageCallback, (LPARAM)&psh); - if (FAILED(hr)) - ERR("AddPages failed\n"); - } - else - { - ERR("Initialize failed\n"); - } - } - - hpsx = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, L"Drive", MAX_PROPERTY_SHEET_PAGE, pDataObj); - if (hpsx) - SHAddFromPropSheetExtArray(hpsx, (LPFNADDPROPSHEETPAGE)AddPropSheetPageCallback, (LPARAM)&psh); - - INT_PTR ret = PropertySheetW(&psh); - - if (hpsx) - SHDestroyPropSheetExtArray(hpsx); - if (pDrvDefExt) - pDrvDefExt->Release(); - - stub.DestroyWindow(); - - return ret != -1; -} - -BOOL -SH_ShowDriveProperties(WCHAR *pwszDrive, IDataObject *pDataObj) -{ - HRESULT hr = SHStrDupW(pwszDrive, &pwszDrive); - if (FAILED_UNEXPECTEDLY(hr)) - return FALSE; - - // Prepare data for thread - DRIVE_PROP_DATA *pData = (DRIVE_PROP_DATA *)SHAlloc(sizeof(*pData)); - if (!pData) - { - SHFree(pwszDrive); - return FALSE; - } - pData->pwszDrive = pwszDrive; - - // Marshall IDataObject to IStream - hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObj, &pData->pStream); - if (SUCCEEDED(hr)) - { - // Run a property sheet in another thread - if (SHCreateThread(ShowDrivePropThreadProc, pData, CTF_COINIT, NULL)) - return TRUE; // Success - - pData->pStream->Release(); - } - SHFree(pData); - SHFree(pwszDrive); - return FALSE; // Failed -} - static VOID InsertDefaultClusterSizeForFs(HWND hwndDlg, PFORMAT_DRIVE_CONTEXT pContext) { diff --git a/dll/win32/shell32/dialogs/drvdefext.cpp b/dll/win32/shell32/dialogs/drvdefext.cpp index c11375e0b88..f26413bea3a 100644 --- a/dll/win32/shell32/dialogs/drvdefext.cpp +++ b/dll/win32/shell32/dialogs/drvdefext.cpp @@ -750,12 +750,13 @@ CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) { HPROPSHEETPAGE hPage; - hPage = SH_CreatePropertySheetPage(IDD_DRIVE_PROPERTIES, - GeneralPageProc, - (LPARAM)this, - NULL); - if (hPage) - pfnAddPage(hPage, lParam); + hPage = SH_CreatePropertySheetPageEx(IDD_DRIVE_PROPERTIES, GeneralPageProc, (LPARAM)this, + NULL, &PropSheetPageLifetimeCallback); + HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + else + AddRef(); // For PropSheetPageLifetimeCallback if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED) { diff --git a/dll/win32/shell32/dialogs/filedefext.cpp b/dll/win32/shell32/dialogs/filedefext.cpp index dd3c5cd0b1e..e8beb7f936a 100644 --- a/dll/win32/shell32/dialogs/filedefext.cpp +++ b/dll/win32/shell32/dialogs/filedefext.cpp @@ -290,34 +290,6 @@ SH_FormatFileSizeWithBytes(const PULARGE_INTEGER lpQwSize, LPWSTR pwszResult, UI return pwszResult; } -/************************************************************************* - * - * SH_CreatePropertySheetPage [Internal] - * - * creates a property sheet page from a resource id - * - */ - -HPROPSHEETPAGE -SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle) -{ - PROPSHEETPAGEW Page; - - memset(&Page, 0x0, sizeof(PROPSHEETPAGEW)); - Page.dwSize = sizeof(PROPSHEETPAGEW); - Page.dwFlags = PSP_DEFAULT; - Page.hInstance = shell32_hInstance; - Page.pszTemplate = MAKEINTRESOURCE(wDialogId); - Page.pfnDlgProc = pfnDlgProc; - Page.lParam = lParam; - Page.pszTitle = pwszTitle; - - if (pwszTitle) - Page.dwFlags |= PSP_USETITLE; - - return CreatePropertySheetPageW(&Page); -} - VOID CFileDefExt::InitOpensWithField(HWND hwndDlg) { @@ -1321,12 +1293,13 @@ CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) HPROPSHEETPAGE hPage; WORD wResId = m_bDir ? IDD_FOLDER_PROPERTIES : IDD_FILE_PROPERTIES; - hPage = SH_CreatePropertySheetPage(wResId, - GeneralPageProc, - (LPARAM)this, - NULL); - if (hPage) - pfnAddPage(hPage, lParam); + hPage = SH_CreatePropertySheetPageEx(wResId, GeneralPageProc, (LPARAM)this, NULL, + &PropSheetPageLifetimeCallback); + HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + else + AddRef(); // For PropSheetPageLifetimeCallback if (!m_bDir && GetFileVersionInfoSizeW(m_wszPath, NULL)) { @@ -1334,8 +1307,7 @@ CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) VersionPageProc, (LPARAM)this, NULL); - if (hPage) - pfnAddPage(hPage, lParam); + AddPropSheetPage(hPage, pfnAddPage, lParam); } if (m_bDir) @@ -1344,8 +1316,7 @@ CFileDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) FolderCustomizePageProc, (LPARAM)this, NULL); - if (hPage) - pfnAddPage(hPage, lParam); + AddPropSheetPage(hPage, pfnAddPage, lParam); } return S_OK; diff --git a/dll/win32/shell32/dialogs/fprop.cpp b/dll/win32/shell32/dialogs/fprop.cpp deleted file mode 100644 index 68872bc4c69..00000000000 --- a/dll/win32/shell32/dialogs/fprop.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Shell Library Functions - * - * Copyright 2005 Johannes Anderwald - * Copyright 2012 Rafal Harabien - * Copyright 2017-2018 Katayama Hirofumi MZ - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "precomp.h" - -WINE_DEFAULT_DEBUG_CHANNEL(shell); - -EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj); - -static UINT -LoadPropSheetHandlers(LPCWSTR pwszPath, PROPSHEETHEADERW *pHeader, UINT cMaxPages, HPSXA *phpsxa, IDataObject *pDataObj) -{ - WCHAR wszBuf[MAX_PATH]; - UINT cPages = 0, i = 0; - - LPWSTR pwszFilename = PathFindFileNameW(pwszPath); - BOOL bDir = PathIsDirectoryW(pwszPath); - - if (bDir) - { - phpsxa[i] = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, L"Folder", cMaxPages - cPages, pDataObj); - cPages += SHAddFromPropSheetExtArray(phpsxa[i++], AddPropSheetPageCallback, (LPARAM)pHeader); - - phpsxa[i] = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, L"Directory", cMaxPages - cPages, pDataObj); - cPages += SHAddFromPropSheetExtArray(phpsxa[i++], AddPropSheetPageCallback, (LPARAM)pHeader); - } - else - { - /* Load property sheet handlers from ext key */ - LPWSTR pwszExt = PathFindExtensionW(pwszFilename); - phpsxa[i] = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, pwszExt, cMaxPages - cPages, pDataObj); - cPages += SHAddFromPropSheetExtArray(phpsxa[i++], AddPropSheetPageCallback, (LPARAM)pHeader); - - /* Load property sheet handlers from prog id key */ - DWORD cbBuf = sizeof(wszBuf); - if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, L"", RRF_RT_REG_SZ, NULL, wszBuf, &cbBuf) == ERROR_SUCCESS) - { - TRACE("EnumPropSheetExt wszBuf %s, pwszExt %s\n", debugstr_w(wszBuf), debugstr_w(pwszExt)); - phpsxa[i] = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszBuf, cMaxPages - cPages, pDataObj); - cPages += SHAddFromPropSheetExtArray(phpsxa[i++], AddPropSheetPageCallback, (LPARAM)pHeader); - } - - /* Add property sheet handlers from "*" key */ - phpsxa[i] = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, L"*", cMaxPages - cPages, pDataObj); - cPages += SHAddFromPropSheetExtArray(phpsxa[i++], AddPropSheetPageCallback, (LPARAM)pHeader); - } - - return cPages; -} - -/************************************************************************* - * - * SH_ShowPropertiesDialog - * - * called from ShellExecuteExW32 - * - * pwszPath contains path of folder/file - * - * TODO: provide button change application type if file has registered type - * make filename field editable and apply changes to filename on close - */ - -BOOL -SH_ShowPropertiesDialog(LPCWSTR pwszPath, IDataObject *pDataObj) -{ - HPSXA hpsxa[3] = {NULL, NULL, NULL}; - CComObject *pFileDefExt = NULL; - - TRACE("SH_ShowPropertiesDialog entered filename %s\n", debugstr_w(pwszPath)); - - if (pwszPath == NULL || !wcslen(pwszPath)) - return FALSE; - - HPROPSHEETPAGE hppages[MAX_PROPERTY_SHEET_PAGE]; - memset(hppages, 0x0, sizeof(HPROPSHEETPAGE) * MAX_PROPERTY_SHEET_PAGE); - - /* Make a copy of path */ - WCHAR wszPath[MAX_PATH]; - StringCbCopyW(wszPath, sizeof(wszPath), pwszPath); - - /* remove trailing \\ at the end of path */ - PathRemoveBackslashW(wszPath); - - CDataObjectHIDA cida(pDataObj); - if (FAILED_UNEXPECTEDLY(cida.hr())) - return FALSE; - - if (cida->cidl == 0) - { - ERR("Empty HIDA\n"); - return FALSE; - } - - /* Handle drives */ - if (_ILIsDrive(HIDA_GetPIDLItem(cida, 0))) - return SH_ShowDriveProperties(wszPath, pDataObj); - - - RECT rcPosition = {CW_USEDEFAULT, CW_USEDEFAULT, 0, 0}; - POINT pt; - if (SUCCEEDED(DataObject_GetOffset(pDataObj, &pt))) - { - rcPosition.left = pt.x; - rcPosition.top = pt.y; - } - - DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION; - DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; - CStubWindow32 stub; - if (!stub.Create(NULL, rcPosition, NULL, style, exstyle)) - { - ERR("StubWindow32 creation failed\n"); - return FALSE; - } - - /* Handle files and folders */ - PROPSHEETHEADERW Header; - memset(&Header, 0x0, sizeof(PROPSHEETHEADERW)); - Header.dwSize = sizeof(PROPSHEETHEADERW); - Header.hwndParent = stub; - Header.dwFlags = PSH_NOCONTEXTHELP | PSH_PROPTITLE; - Header.phpage = hppages; - Header.pszCaption = PathFindFileNameW(wszPath); - - HRESULT hr = CComObject::CreateInstance(&pFileDefExt); - if (SUCCEEDED(hr)) - { - pFileDefExt->AddRef(); // CreateInstance returns object with 0 ref count - hr = pFileDefExt->Initialize(HIDA_GetPIDLFolder(cida), pDataObj, NULL); - if (!FAILED_UNEXPECTEDLY(hr)) - { - hr = pFileDefExt->AddPages(AddPropSheetPageCallback, (LPARAM)&Header); - if (FAILED_UNEXPECTEDLY(hr)) - { - ERR("AddPages failed\n"); - return FALSE; - } - } - else - { - ERR("Initialize failed\n"); - return FALSE; - } - } - - LoadPropSheetHandlers(wszPath, &Header, MAX_PROPERTY_SHEET_PAGE - 1, hpsxa, pDataObj); - - INT_PTR Result = PropertySheetW(&Header); - - for (UINT i = 0; i < 3; ++i) - if (hpsxa[i]) - SHDestroyPropSheetExtArray(hpsxa[i]); - if (pFileDefExt) - pFileDefExt->Release(); - - stub.DestroyWindow(); - - return (Result != -1); -} - -/*EOF */ diff --git a/dll/win32/shell32/dialogs/item_prop.cpp b/dll/win32/shell32/dialogs/item_prop.cpp new file mode 100644 index 00000000000..90160a8062e --- /dev/null +++ b/dll/win32/shell32/dialogs/item_prop.cpp @@ -0,0 +1,179 @@ +/* + * PROJECT: shell32 + * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) + * PURPOSE: FileSystem PropertySheet implementation + * COPYRIGHT: Copyright 2024 Whindmar Saksit + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +static HRESULT +SHELL_GetCaptionFromDataObject(IDataObject *pDO, LPWSTR Buf, UINT cchBuf) +{ + HRESULT hr = E_INVALIDARG; + if (PIDLIST_ABSOLUTE pidl = SHELL_DataObject_ILCloneFullItem(pDO, 0)) + { + hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, Buf, cchBuf, NULL); + ILFree(pidl); + } + return hr; +} + +struct ShellPropSheetDialog +{ + typedef void (CALLBACK*PFNINITIALIZE)(LPCWSTR InitString, IDataObject *pDO, + HKEY *hKeys, UINT *cKeys); + + static HRESULT Show(const CLSID *pClsidDefault, IDataObject *pDO, + PFNINITIALIZE InitFunc, LPCWSTR InitString) + { + HRESULT hr; + CRegKeyHandleArray keys; + if (InitFunc) + InitFunc(InitString, pDO, keys, keys); + WCHAR szCaption[MAX_PATH], *pszCaption = NULL; + if (SUCCEEDED(SHELL_GetCaptionFromDataObject(pDO, szCaption, _countof(szCaption)))) + pszCaption = szCaption; + hr = SHOpenPropSheetW(pszCaption, keys, keys, pClsidDefault, pDO, NULL, NULL) ? S_OK : E_FAIL; + return hr; + } + + struct DATA + { + PFNINITIALIZE InitFunc; + LPWSTR InitString; + CLSID ClsidDefault; + const CLSID *pClsidDefault; + IStream *pObjStream; + }; + + static void FreeData(DATA *pData) + { + if (pData->InitString) + SHFree(pData->InitString); + SHFree(pData); + } + + static HRESULT ShowAsync(const CLSID *pClsidDefault, IDataObject *pDO, + PFNINITIALIZE InitFunc, LPCWSTR InitString) + { + DATA *pData = (DATA*)SHAlloc(sizeof(*pData)); + if (!pData) + return E_OUTOFMEMORY; + ZeroMemory(pData, sizeof(*pData)); + pData->InitFunc = InitFunc; + if (InitString && FAILED(SHStrDupW(InitString, &pData->InitString))) + { + FreeData(pData); + return E_OUTOFMEMORY; + } + if (pClsidDefault) + { + pData->ClsidDefault = *pClsidDefault; + pData->pClsidDefault = &pData->ClsidDefault; + } + + HRESULT hr = S_OK; + if (pDO) + hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDO, &pData->pObjStream); + + const UINT flags = CTF_COINIT | CTF_PROCESS_REF | CTF_INSIST; + if (SUCCEEDED(hr) && !SHCreateThread(ShowPropertiesThread, pData, flags, NULL)) + { + if (pData->pObjStream) + pData->pObjStream->Release(); + hr = E_FAIL; + } + if (FAILED(hr)) + FreeData(pData); + return hr; + } + + static DWORD CALLBACK ShowPropertiesThread(LPVOID Param) + { + DATA *pData = (DATA*)Param; + CComPtr pDO; + if (pData->pObjStream) + CoGetInterfaceAndReleaseStream(pData->pObjStream, IID_PPV_ARG(IDataObject, &pDO)); + Show(pData->pClsidDefault, pDO, pData->InitFunc, pData->InitString); + FreeData(pData); + return 0; + } +}; + +static void CALLBACK +FSFolderItemPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys) +{ + UNREFERENCED_PARAMETER(InitString); + CDataObjectHIDA cida(pDO); + if (SUCCEEDED(cida.hr()) && cida->cidl) + { + PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0); + AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages + } +} + +static void CALLBACK +ClassPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *hKeys, UINT *cKeys) +{ + UNREFERENCED_PARAMETER(pDO); + AddClassKeyToArray(InitString, hKeys, cKeys); // Add pages from HKCR\%ProgId% (with shellex\PropertySheetHandlers appended later) +} + +HRESULT +SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO) +{ + CDataObjectHIDA cida(pDO); + HRESULT hr = cida.hr(); + if (FAILED_UNEXPECTEDLY(hr)) + return hr; + + const CLSID *pClsid = NULL; + ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL; + LPCWSTR InitString = NULL; + + if (_ILIsDrive(HIDA_GetPIDLItem(cida, 0))) + { + pClsid = &CLSID_ShellDrvDefExt; + InitFunc = ClassPropDialogInitCallback; + InitString = L"Drive"; + } + else + { + pClsid = &CLSID_ShellFileDefExt; + InitFunc = FSFolderItemPropDialogInitCallback; + } + ShellPropSheetDialog Dialog; + return Dialog.ShowAsync(pClsid, pDO, InitFunc, InitString); +} + +HRESULT +SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO) +{ + WCHAR ClassBuf[6 + 38 + 1] = L"CLSID\\"; + StringFromGUID2(*pClsid, ClassBuf + 6, 38 + 1); + ShellPropSheetDialog Dialog; + return Dialog.ShowAsync(NULL, pDO, ClassPropDialogInitCallback, ClassBuf); +} + +HRESULT +SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl) +{ + assert(pidl); + + CComHeapPtr alloc; + if (IS_INTRESOURCE(pidl)) + { + HRESULT hr = SHGetSpecialFolderLocation(NULL, LOWORD(pidl), const_cast(&pidl)); + if (FAILED(hr)) + return hr; + alloc.Attach(const_cast(pidl)); + } + SHELLEXECUTEINFOA sei = { + sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK, NULL, "properties", + NULL, NULL, NULL, SW_SHOWNORMAL, NULL, const_cast(pidl) + }; + return ShellExecuteExA(&sei) ? S_OK : HResultFromWin32(GetLastError()); +} diff --git a/dll/win32/shell32/dialogs/recycler_prop.cpp b/dll/win32/shell32/dialogs/recycler_prop.cpp index ea1c9ca2cef..d7f79251a3e 100644 --- a/dll/win32/shell32/dialogs/recycler_prop.cpp +++ b/dll/win32/shell32/dialogs/recycler_prop.cpp @@ -401,30 +401,8 @@ RecycleBinDlg( return FALSE; } -BOOL SH_ShowRecycleBinProperties(WCHAR sDrive) +HRESULT RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) { - HPROPSHEETPAGE hpsp[1]; - PROPSHEETHEADERW psh; - HPROPSHEETPAGE hprop; - INT_PTR ret; - - ZeroMemory(&psh, sizeof(psh)); - psh.dwSize = sizeof(psh); - psh.dwFlags = PSP_DEFAULT | PSH_PROPTITLE; - psh.pszCaption = MAKEINTRESOURCEW(IDS_RECYCLEBIN_FOLDER_NAME); - psh.hwndParent = NULL; - psh.phpage = hpsp; - psh.hInstance = shell32_hInstance; - - hprop = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, (LPARAM)sDrive, NULL); - if (!hprop) - { - ERR("Failed to create property sheet\n"); - return FALSE; - } - hpsp[psh.nPages] = hprop; - psh.nPages++; - - ret = PropertySheetW(&psh); - return (ret >= 0); + HPROPSHEETPAGE hpsp = SH_CreatePropertySheetPage(IDD_RECYCLE_BIN_PROPERTIES, RecycleBinDlg, 0, NULL); + return AddPropSheetPage(hpsp, pfnAddPage, lParam); } diff --git a/dll/win32/shell32/folders/CDrivesFolder.cpp b/dll/win32/shell32/folders/CDrivesFolder.cpp index fd41debc914..040511f03e9 100644 --- a/dll/win32/shell32/folders/CDrivesFolder.cpp +++ b/dll/win32/shell32/folders/CDrivesFolder.cpp @@ -186,117 +186,22 @@ static BOOL DoEjectDrive(const WCHAR *physical, UINT nDriveType, INT *pnStringID return bResult; } -// A callback function for finding the stub windows. -static BOOL CALLBACK -EnumStubProc(HWND hwnd, LPARAM lParam) +static DWORD CALLBACK DoFormatDriveThread(LPVOID args) { - CSimpleArray *pStubs = reinterpret_cast *>(lParam); - - WCHAR szClass[64]; - GetClassNameW(hwnd, szClass, _countof(szClass)); - - if (lstrcmpiW(szClass, L"StubWindow32") == 0) - { - pStubs->Add(hwnd); - } - - return TRUE; -} - -// Another callback function to find the owned window of the stub window. -static BOOL CALLBACK -EnumStubProc2(HWND hwnd, LPARAM lParam) -{ - HWND *phwnd = reinterpret_cast(lParam); - - if (phwnd[0] == GetWindow(hwnd, GW_OWNER)) - { - phwnd[1] = hwnd; - return FALSE; - } - - return TRUE; -} - -// Parameters for format_drive_thread function below. -struct THREAD_PARAMS -{ - UINT nDriveNumber; -}; - -static unsigned __stdcall format_drive_thread(void *args) -{ - THREAD_PARAMS *params = (THREAD_PARAMS *)args; - UINT nDriveNumber = params->nDriveNumber; - LONG_PTR nProp = nDriveNumber | 0x7F00; - - // Search the stub windows that already exist. - CSimpleArray old_stubs; - EnumWindows(EnumStubProc, (LPARAM)&old_stubs); - - for (INT n = 0; n < old_stubs.GetSize(); ++n) - { - HWND hwndStub = old_stubs[n]; - - // The target stub window has the prop. - if (GetPropW(hwndStub, L"DriveNumber") == (HANDLE)nProp) - { - // Found. - HWND ahwnd[2]; - ahwnd[0] = hwndStub; - ahwnd[1] = NULL; - EnumWindows(EnumStubProc2, (LPARAM)ahwnd); - - // Activate. - BringWindowToTop(ahwnd[1]); - - delete params; - return 0; - } - } - - // Create a stub window. - DWORD style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION; - DWORD exstyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; + UINT nDrive = PtrToUlong(args); + WCHAR szPath[] = { LOWORD(L'A' + nDrive), L'\0' }; // Arbitrary, just needs to include nDrive CStubWindow32 stub; - if (!stub.Create(NULL, NULL, NULL, style, exstyle)) - { - ERR("StubWindow32 creation failed\n"); - delete params; - return 0; - } - - // Add prop to the target stub window. - SetPropW(stub, L"DriveNumber", (HANDLE)nProp); - - // Do format. - SHFormatDrive(stub, nDriveNumber, SHFMT_ID_DEFAULT, 0); - - // Clean up. - RemovePropW(stub, L"DriveNumber"); - stub.DestroyWindow(); - delete params; - - return 0; + HRESULT hr = stub.CreateStub(CStubWindow32::TYPE_FORMATDRIVE, szPath, NULL); + if (FAILED(hr)) + return hr; + SHFormatDrive(stub, nDrive, SHFMT_ID_DEFAULT, 0); + return stub.DestroyWindow(); } -static HRESULT DoFormatDrive(HWND hwnd, UINT nDriveNumber) +static HRESULT DoFormatDriveAsync(HWND hwnd, UINT nDrive) { - THREAD_PARAMS *params = new THREAD_PARAMS; - params->nDriveNumber = nDriveNumber; - - // Create thread to avoid locked. - unsigned tid; - HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, format_drive_thread, params, 0, &tid); - if (hThread == NULL) - { - delete params; - return E_FAIL; - } - - CloseHandle(hThread); - - return S_OK; + BOOL succ = SHCreateThread(DoFormatDriveThread, UlongToPtr(nDrive), CTF_PROCESS_REF, NULL); + return succ ? S_OK : E_FAIL; } HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf, @@ -388,20 +293,15 @@ HRESULT CALLBACK DrivesContextMenuCallback(IShellFolder *psf, if (wParam == DFM_CMD_PROPERTIES) { - // pdtobj should be valid at this point! ATLASSERT(pdtobj); - hr = SH_ShowDriveProperties(wszBuf, pdtobj) ? S_OK : E_UNEXPECTED; - if (FAILED(hr)) - { - dwError = ERROR_CAN_NOT_COMPLETE; - nStringID = IDS_CANTSHOWPROPERTIES; - } + hr = SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj); + // Not setting nStringID because SHOpenPropSheet already displayed an error box } else { if (wParam == CMDID_FORMAT) { - hr = DoFormatDrive(hwnd, szDrive[0] - 'A'); + hr = DoFormatDriveAsync(hwnd, szDrive[0] - 'A'); } else if (wParam == CMDID_EJECT) { diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index bb73ec85407..9ae1f53da1f 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -1959,25 +1959,7 @@ HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObjec { if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES) { - // Create an data object - CComHeapPtr pidlChild(ILClone(ILFindLastID(m_pidlRoot))); - CComHeapPtr pidlParent(ILClone(m_pidlRoot)); - ILRemoveLastID(pidlParent); - - CComPtr pDataObj; - HRESULT hr = SHCreateDataObject(pidlParent, 1, &pidlChild.m_pData, NULL, IID_PPV_ARG(IDataObject, &pDataObj)); - if (!FAILED_UNEXPECTEDLY(hr)) - { - // Ask for a title to display - CComHeapPtr wszName; - if (!FAILED_UNEXPECTEDLY(SHGetNameFromIDList(m_pidlRoot, SIGDN_PARENTRELATIVEPARSING, &wszName))) - { - BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObj); - if (!bSuccess) - ERR("SH_ShowPropertiesDialog failed\n"); - } - } - return hr; + return SHELL_ShowItemIDListProperties(m_pidlRoot); } else if (uMsg == DFM_MERGECONTEXTMENU) { diff --git a/dll/win32/shell32/folders/CRecycleBin.cpp b/dll/win32/shell32/folders/CRecycleBin.cpp index 78b3d033ba2..a9660e216a8 100644 --- a/dll/win32/shell32/folders/CRecycleBin.cpp +++ b/dll/win32/shell32/folders/CRecycleBin.cpp @@ -919,9 +919,8 @@ HRESULT WINAPI CRecycleBin::GetCommandString(UINT_PTR idCommand, UINT uFlags, UI HRESULT WINAPI CRecycleBin::AddPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) { - FIXME("%p %p %lu\n", this, pfnAddPage, lParam); - - return E_NOTIMPL; + extern HRESULT RecycleBin_AddPropSheetPages(LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam); + return RecycleBin_AddPropSheetPages(pfnAddPage, lParam); } HRESULT WINAPI CRecycleBin::ReplacePage(EXPPS uPageID, LPFNSVADDPROPSHEETPAGE pfnReplaceWith, LPARAM lParam) diff --git a/dll/win32/shell32/folders/CRegFolder.cpp b/dll/win32/shell32/folders/CRegFolder.cpp index 510727a6b67..b983b5496a2 100644 --- a/dll/win32/shell32/folders/CRegFolder.cpp +++ b/dll/win32/shell32/folders/CRegFolder.cpp @@ -881,6 +881,7 @@ static HRESULT CALLBACK RegFolderContextMenuCallback(IShellFolder *psf, HWND hwn if (FAILED_UNEXPECTEDLY(hr)) return hr; + const CLSID* pCLSID; CRegFolder *pRegFolder = static_cast(psf); const REQUIREDREGITEM* pRequired = pRegFolder->IsRequiredItem(apidl[0]); if (pRequired && pRequired->pszCpl) @@ -895,10 +896,17 @@ static HRESULT CALLBACK RegFolderContextMenuCallback(IShellFolder *psf, HWND hwn hr = SHELL_ExecuteControlPanelCPL(hwnd, L"desk.cpl") ? S_OK : E_FAIL; } #endif - else if (_ILIsDesktop(pidlFolder) && _ILIsBitBucket(apidl[0])) + else if ((pCLSID = pRegFolder->IsRegItem(apidl[0])) != NULL) { - FIXME("Use SHOpenPropSheet on Recyclers PropertySheetHandlers from the registry\n"); - hr = SH_ShowRecycleBinProperties(L'C') ? S_OK : E_FAIL; + if (CLSID_MyDocuments != *pCLSID) + { + hr = SHELL32_ShowShellExtensionProperties(pCLSID, pdtobj); + } + else + { + FIXME("ROS MyDocs must implement IShellPropSheetExt\n"); + hr = S_FALSE; // Just display the filesystem properties + } } else { diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 614478a3d08..a28c023d244 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -126,10 +126,10 @@ extern const GUID CLSID_UnixDosFolder; extern const GUID SHELL32_AdvtShortcutProduct; extern const GUID SHELL32_AdvtShortcutComponent; -#define MAX_PROPERTY_SHEET_PAGE 32 - #define VERBKEY_CCHMAX 64 // Note: 63+\0 seems to be the limit on XP +#define MAX_PROPERTY_SHEET_PAGE 32 + extern inline BOOL CALLBACK @@ -146,8 +146,36 @@ AddPropSheetPageCallback(HPROPSHEETPAGE hPage, LPARAM lParam) return FALSE; } +static inline HRESULT +AddPropSheetPage(HPROPSHEETPAGE hPage, LPFNSVADDPROPSHEETPAGE pfnAddPage, LPARAM lParam) +{ + if (!hPage) + return E_FAIL; + if (pfnAddPage(hPage, lParam)) + return S_OK; + DestroyPropertySheetPage(hPage); + return E_FAIL; +} + +template static UINT CALLBACK +PropSheetPageLifetimeCallback(HWND hWnd, UINT uMsg, PROPSHEETPAGEW *pPSP) +{ + if (uMsg == PSPCB_RELEASE) + ((T*)(pPSP->lParam))->Release(); + return TRUE; +} + +EXTERN_C HRESULT WINAPI +SHMultiFileProperties(IDataObject *pDataObject, DWORD dwFlags); HRESULT SHELL32_ShowPropertiesDialog(IDataObject *pdtobj); +HRESULT +SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO); +HRESULT +SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO); +HRESULT +SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl); + HRESULT SHELL32_DefaultContextMenuCallBack(IShellFolder *psf, IDataObject *pdo, UINT msg); UINT @@ -167,12 +195,29 @@ static inline UINT SeeFlagsToCmicFlags(UINT flags) // CStubWindow32 --- The owner window of file property sheets. // This window hides taskbar button of property sheet. +#define CSTUBWINDOW32_CLASSNAME _T("StubWindow32") class CStubWindow32 : public CWindowImpl { + static HWND FindStubWindow(UINT Type, LPCWSTR Path); public: - DECLARE_WND_CLASS_EX(_T("StubWindow32"), 0, COLOR_WINDOWTEXT) + DECLARE_WND_CLASS_EX(CSTUBWINDOW32_CLASSNAME, 0, COLOR_WINDOWTEXT) + enum { + TYPE_FORMATDRIVE = 1, + TYPE_PROPERTYSHEET, + }; + static LPCWSTR GetTypePropName() { return L"StubType"; } + HRESULT CreateStub(UINT Type, LPCWSTR Path, const POINT *pPt); + + LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + if (HICON hIco = (HICON)SendMessage(WM_SETICON, ICON_BIG, NULL)) + DestroyIcon(hIco); + ::RemovePropW(m_hWnd, GetTypePropName()); + return 0; + } BEGIN_MSG_MAP(CStubWindow32) + MESSAGE_HANDLER(WM_DESTROY, OnDestroy) END_MSG_MAP() }; diff --git a/dll/win32/shell32/propsheet.cpp b/dll/win32/shell32/propsheet.cpp new file mode 100644 index 00000000000..3fccaa0ac1a --- /dev/null +++ b/dll/win32/shell32/propsheet.cpp @@ -0,0 +1,211 @@ +/* + * PROJECT: shell32 + * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+) + * PURPOSE: SHOpenPropSheetW implementation + * COPYRIGHT: Copyright 2024 Whindmar Saksit + */ + +#include "precomp.h" + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +static HRESULT +SHELL_GetShellExtensionRegCLSID(HKEY hKey, LPCWSTR KeyName, CLSID &clsid) +{ + // First try the key name + if (SUCCEEDED(SHCLSIDFromStringW(KeyName, &clsid))) + return S_OK; + WCHAR buf[42]; + DWORD cb = sizeof(buf); + // and then the default value + DWORD err = RegGetValueW(hKey, KeyName, NULL, RRF_RT_REG_SZ, NULL, buf, &cb); + return !err ? SHCLSIDFromStringW(buf, &clsid) : HRESULT_FROM_WIN32(err); +} + +static HRESULT +SHELL_InitializeExtension(REFCLSID clsid, PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDO, HKEY hkeyProgID, REFIID riid, void **ppv) +{ + *ppv = NULL; + IUnknown *pUnk; + HRESULT hr = SHCoCreateInstance(NULL, &clsid, NULL, riid, (void**)&pUnk); + if (SUCCEEDED(hr)) + { + CComPtr Init; + hr = pUnk->QueryInterface(IID_PPV_ARG(IShellExtInit, &Init)); + if (SUCCEEDED(hr)) + hr = Init->Initialize(pidlFolder, pDO, hkeyProgID); + + if (SUCCEEDED(hr)) + *ppv = (void*)pUnk; + else + pUnk->Release(); + } + return hr; +} + +static HRESULT +AddPropSheetHandlerPages(REFCLSID clsid, IDataObject *pDO, HKEY hkeyProgID, PROPSHEETHEADERW &psh) +{ + CComPtr SheetExt; + HRESULT hr = SHELL_InitializeExtension(clsid, NULL, pDO, hkeyProgID, IID_PPV_ARG(IShellPropSheetExt, &SheetExt)); + if (SUCCEEDED(hr)) + { + UINT OldCount = psh.nPages; + hr = SheetExt->AddPages(AddPropSheetPageCallback, (LPARAM)&psh); + // The returned index is one-based (relative to this extension). + // See https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishellpropsheetext-addpages + if (hr > 0) + { + hr += OldCount; + psh.nStartPage = hr - 1; + } + } + return hr; +} + +static HRESULT +SHELL_CreatePropSheetStubWindow(CStubWindow32 &stub, PCIDLIST_ABSOLUTE pidl, const POINT *pPt) +{ + PWSTR Path; + if (!pidl || FAILED(SHGetNameFromIDList(pidl, SIGDN_DESKTOPABSOLUTEPARSING, &Path))) + Path = NULL; // If we can't get a path, we simply will not be able to reuse this window + + HRESULT hr = stub.CreateStub(CStubWindow32::TYPE_PROPERTYSHEET, Path, pPt); + SHFree(Path); + UINT flags = SHGFI_ICON | SHGFI_ADDOVERLAYS; + SHFILEINFO sfi; + if (SUCCEEDED(hr) && SHGetFileInfoW((LPWSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | flags)) + stub.SendMessage(WM_SETICON, ICON_BIG, (LPARAM)sfi.hIcon); + return hr; +} + +/************************************************************************* + * SHELL32_PropertySheet [INTERNAL] + * PropertySheetW with stub window. + */ +static INT_PTR +SHELL32_PropertySheet(LPPROPSHEETHEADERW ppsh, IDataObject *pDO) +{ + CStubWindow32 stub; + POINT pt, *ppt = NULL; + if (pDO && SUCCEEDED(DataObject_GetOffset(pDO, &pt))) + ppt = &pt; + PIDLIST_ABSOLUTE pidl = SHELL_DataObject_ILCloneFullItem(pDO, 0); + HRESULT hr = SHELL_CreatePropSheetStubWindow(stub, pidl, ppt); + ILFree(pidl); + if (FAILED(hr)) + return hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS) ? 0 : -1; + + INT_PTR Result = -1; + ppsh->hwndParent = stub; + if (ppsh->nPages) + { + Result = PropertySheetW(ppsh); + } + else + { + WCHAR szFormat[128], szMessage[_countof(szFormat) + 42]; + LoadStringW(shell32_hInstance, IDS_CANTSHOWPROPERTIES, szFormat, _countof(szFormat)); + wsprintfW(szMessage, szFormat, ERROR_CAN_NOT_COMPLETE); + MessageBoxW(NULL, szMessage, NULL, MB_ICONERROR); + } + stub.DestroyWindow(); + return Result; +} + +/************************************************************************* + * SHELL32_OpenPropSheet [INTERNAL] + * The real implementation of SHOpenPropSheetW. + */ +static BOOL +SHELL32_OpenPropSheet(LPCWSTR pszCaption, HKEY *ahKeys, UINT cKeys, + const CLSID *pclsidDefault, IDataObject *pDO, LPCWSTR pStartPage) +{ + HKEY hKeyProgID = cKeys ? ahKeys[cKeys - 1] : NULL; // Windows uses the last key for some reason + HPROPSHEETPAGE hppages[MAX_PROPERTY_SHEET_PAGE]; + PROPSHEETHEADERW psh = { sizeof(psh), PSH_PROPTITLE }; + psh.phpage = hppages; + psh.hInstance = shell32_hInstance; + psh.pszCaption = pszCaption; + psh.pStartPage = pStartPage; + + if (pclsidDefault) + AddPropSheetHandlerPages(*pclsidDefault, pDO, hKeyProgID, psh); + + for (UINT i = 0; i < cKeys; ++i) + { + // Note: We can't use SHCreatePropSheetExtArrayEx because we need the AddPages() return value (see AddPropSheetHandlerPages). + HKEY hKey; + if (RegOpenKeyExW(ahKeys[i], L"shellex\\PropertySheetHandlers", 0, KEY_ENUMERATE_SUB_KEYS, &hKey)) + continue; + for (UINT index = 0;; ++index) + { + WCHAR KeyName[MAX_PATH]; + LRESULT err = RegEnumKeyW(hKey, index, KeyName, _countof(KeyName)); + KeyName[_countof(KeyName)-1] = UNICODE_NULL; + if (err == ERROR_MORE_DATA) + continue; + if (err) + break; + CLSID clsid; + if (SUCCEEDED(SHELL_GetShellExtensionRegCLSID(hKey, KeyName, clsid))) + AddPropSheetHandlerPages(clsid, pDO, hKeyProgID, psh); + } + RegCloseKey(hKey); + } + + if (pStartPage == psh.pStartPage && pStartPage) + psh.dwFlags |= PSH_USEPSTARTPAGE; + INT_PTR Result = SHELL32_PropertySheet(&psh, pDO); + return (Result != -1); +} + +/************************************************************************* + * SHOpenPropSheetW [SHELL32.80] + * + * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheetw + */ +EXTERN_C BOOL WINAPI +SHOpenPropSheetW( + _In_opt_ LPCWSTR pszCaption, + _In_opt_ HKEY *ahKeys, + _In_ UINT cKeys, + _In_ const CLSID *pclsidDefault, + _In_ IDataObject *pDataObject, + _In_opt_ IShellBrowser *pShellBrowser, + _In_opt_ LPCWSTR pszStartPage) +{ + UNREFERENCED_PARAMETER(pShellBrowser); /* MSDN says "Not used". */ + return SHELL32_OpenPropSheet(pszCaption, ahKeys, cKeys, pclsidDefault, pDataObject, pszStartPage); +} + +/************************************************************************* + * SH_CreatePropertySheetPage [Internal] + * + * creates a property sheet page from a resource id + */ +HPROPSHEETPAGE +SH_CreatePropertySheetPageEx(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, + LPCWSTR pwszTitle, LPFNPSPCALLBACK Callback) +{ + PROPSHEETPAGEW Page = { sizeof(Page), PSP_DEFAULT, shell32_hInstance }; + Page.pszTemplate = MAKEINTRESOURCEW(wDialogId); + Page.pfnDlgProc = pfnDlgProc; + Page.lParam = lParam; + Page.pszTitle = pwszTitle; + Page.pfnCallback = Callback; + + if (pwszTitle) + Page.dwFlags |= PSP_USETITLE; + + if (Callback) + Page.dwFlags |= PSP_USECALLBACK; + + return CreatePropertySheetPageW(&Page); +} + +HPROPSHEETPAGE +SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle) +{ + return SH_CreatePropertySheetPageEx(wDialogId, pfnDlgProc, lParam, pwszTitle, NULL); +} diff --git a/dll/win32/shell32/shell32.cpp b/dll/win32/shell32/shell32.cpp index 3d55c35794f..a43afe2dc60 100644 --- a/dll/win32/shell32/shell32.cpp +++ b/dll/win32/shell32/shell32.cpp @@ -275,6 +275,8 @@ BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_ShellFSFolder, CFSFolder) OBJECT_ENTRY(CLSID_MyComputer, CDrivesFolder) OBJECT_ENTRY(CLSID_ShellDesktop, CDesktopFolder) + OBJECT_ENTRY(CLSID_ShellFileDefExt, CFileDefExt) + OBJECT_ENTRY(CLSID_ShellDrvDefExt, CDrvDefExt) OBJECT_ENTRY(CLSID_ShellItem, CShellItem) OBJECT_ENTRY(CLSID_ShellLink, CShellLink) OBJECT_ENTRY(CLSID_Shell, CShellDispatch) diff --git a/dll/win32/shell32/shfldr.h b/dll/win32/shell32/shfldr.h index ff397123b8e..b9cd4012864 100644 --- a/dll/win32/shell32/shfldr.h +++ b/dll/win32/shell32/shfldr.h @@ -126,8 +126,6 @@ HRESULT SHELL32_BindToSF (LPCITEMIDLIST pidlRoot, PERSIST_FOLDER_TARGET_INFO* pp extern "C" BOOL HCR_RegOpenClassIDKey(REFIID riid, HKEY *hkey); -void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys); - HRESULT CDefViewBckgrndMenu_CreateInstance(IShellFolder* psf, REFIID riid, void **ppv); HRESULT SH_GetApidlFromDataObject(IDataObject *pDataObject, PIDLIST_ABSOLUTE* ppidlfolder, PUITEMID_CHILD **apidlItems, UINT *pcidl); @@ -156,11 +154,26 @@ static __inline int SHELL32_GUIDToStringW (REFGUID guid, LPWSTR str) void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags); BOOL SHELL_FS_HideExtension(LPCWSTR pwszPath); +void CloseRegKeyArray(HKEY* array, UINT cKeys); LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys); LSTATUS AddClsidKeyToArray(REFCLSID clsid, HKEY* array, UINT* cKeys); +void AddFSClassKeysToArray(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, HKEY* array, UINT* cKeys); #ifdef __cplusplus +struct CRegKeyHandleArray +{ + HKEY hKeys[16]; + UINT cKeys; + + CRegKeyHandleArray() : cKeys(0) {} + ~CRegKeyHandleArray() { CloseRegKeyArray(hKeys, cKeys); } + operator HKEY*() { return hKeys; } + operator UINT*() { return &cKeys; } + operator UINT() { return cKeys; } + HKEY& operator [](SIZE_T i) { return hKeys[i]; } +}; + HRESULT inline SHSetStrRet(LPSTRRET pStrRet, DWORD resId) { return SHSetStrRet(pStrRet, shell32_hInstance, resId); diff --git a/dll/win32/shell32/shldataobject.cpp b/dll/win32/shell32/shldataobject.cpp index fb50c78e7ac..3a7d651424f 100644 --- a/dll/win32/shell32/shldataobject.cpp +++ b/dll/win32/shell32/shldataobject.cpp @@ -106,6 +106,8 @@ PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index) PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index) { + if (!pDO) + return NULL; CDataObjectHIDA cida(pDO); return SUCCEEDED(cida.hr()) ? SHELL_CIDA_ILCloneFull(cida, Index) : NULL; } diff --git a/dll/win32/shell32/shlfolder.cpp b/dll/win32/shell32/shlfolder.cpp index 37658ced2aa..4e08e19f54f 100644 --- a/dll/win32/shell32/shlfolder.cpp +++ b/dll/win32/shell32/shlfolder.cpp @@ -302,6 +302,12 @@ HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST return MAKE_COMPARE_HRESULT(ret); } +void CloseRegKeyArray(HKEY* array, UINT cKeys) +{ + for (UINT i = 0; i < cKeys; ++i) + RegCloseKey(array[i]); +} + LSTATUS AddClassKeyToArray(const WCHAR* szClass, HKEY* array, UINT* cKeys) { if (*cKeys >= 16) @@ -495,36 +501,6 @@ SHOpenFolderAndSelectItems(PCIDLIST_ABSOLUTE pidlFolder, return E_FAIL; } -static -DWORD WINAPI -_ShowPropertiesDialogThread(LPVOID lpParameter) -{ - CComPtr pDataObject; - pDataObject.Attach((IDataObject*)lpParameter); - - CDataObjectHIDA cida(pDataObject); - - if (FAILED_UNEXPECTEDLY(cida.hr())) - return cida.hr(); - - if (cida->cidl > 1) - { - ERR("SHMultiFileProperties is not yet implemented\n"); - return E_FAIL; - } - - CComHeapPtr completePidl(ILCombine(HIDA_GetPIDLFolder(cida), HIDA_GetPIDLItem(cida, 0))); - CComHeapPtr wszName; - if (FAILED_UNEXPECTEDLY(SHGetNameFromIDList(completePidl, SIGDN_PARENTRELATIVEPARSING, &wszName))) - return 0; - - BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObject); - if (!bSuccess) - ERR("SH_ShowPropertiesDialog failed\n"); - - return 0; -} - /* * for internal use */ @@ -534,16 +510,15 @@ SHELL32_ShowPropertiesDialog(IDataObject *pdtobj) if (!pdtobj) return E_INVALIDARG; - pdtobj->AddRef(); - if (!SHCreateThread(_ShowPropertiesDialogThread, pdtobj, CTF_INSIST | CTF_COINIT | CTF_PROCESS_REF, NULL)) + CDataObjectHIDA cida(pdtobj); + if (FAILED_UNEXPECTEDLY(cida.hr())) + return cida.hr(); + if (cida->cidl > 1) { - pdtobj->Release(); - return HResultFromWin32(GetLastError()); - } - else - { - return S_OK; + ERR("SHMultiFileProperties is not yet implemented\n"); + return E_FAIL; } + return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj); } HRESULT diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index 6b6e1416788..77e674e795f 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -210,25 +210,6 @@ SHGetSetFolderCustomSettingsA(LPSHFOLDERCUSTOMSETTINGSA pfcs, return E_FAIL; } -/************************************************************************* - * SHOpenPropSheetW [SHELL32.80] - * - * @see https://learn.microsoft.com/en-us/windows/win32/api/shlobj/nf-shlobj-shopenpropsheetw - */ -BOOL WINAPI -SHOpenPropSheetW( - _In_opt_ LPCWSTR pszCaption, - _In_opt_ HKEY *ahKeys, - _In_ UINT cKeys, - _In_ const CLSID *pclsidDefault, - _In_ IDataObject *pDataObject, - _In_opt_ IShellBrowser *pShellBrowser, - _In_opt_ LPCWSTR pszStartPage) -{ - FIXME("SHOpenPropSheetW() stub\n"); - return FALSE; -} - /* * Unimplemented */ diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 3fdce2eb5e3..ce2f6f40834 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -9,6 +9,39 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); +HWND +CStubWindow32::FindStubWindow(UINT Type, LPCWSTR Path) +{ + for (HWND hWnd, hWndAfter = NULL;;) + { + hWnd = hWndAfter = FindWindowExW(NULL, hWndAfter, CSTUBWINDOW32_CLASSNAME, Path); + if (!hWnd || !Path) + return NULL; + if (GetPropW(hWnd, GetTypePropName()) == ULongToHandle(Type)) + return hWnd; + } +} + +HRESULT +CStubWindow32::CreateStub(UINT Type, LPCWSTR Path, const POINT *pPt) +{ + if (HWND hWnd = FindStubWindow(Type, Path)) + { + ::SwitchToThisWindow(::GetLastActivePopup(hWnd), TRUE); + return HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + } + RECT rcPosition = { pPt ? pPt->x : CW_USEDEFAULT, pPt ? pPt->y : CW_USEDEFAULT, 0, 0 }; + DWORD Style = WS_DISABLED | WS_CLIPSIBLINGS | WS_CAPTION; + DWORD ExStyle = WS_EX_WINDOWEDGE | WS_EX_APPWINDOW; + if (!Create(NULL, rcPosition, Path, Style, ExStyle)) + { + ERR("StubWindow32 creation failed\n"); + return E_FAIL; + } + ::SetPropW(*this, GetTypePropName(), ULongToHandle(Type)); + return S_OK; +} + HRESULT SHILClone( _In_opt_ LPCITEMIDLIST pidl, diff --git a/dll/win32/shell32/wine/shell32_main.h b/dll/win32/shell32/wine/shell32_main.h index 4392a50659a..d40e1a164b3 100644 --- a/dll/win32/shell32/wine/shell32_main.h +++ b/dll/win32/shell32/wine/shell32_main.h @@ -198,12 +198,9 @@ HRESULT SHELL_RegisterShellFolders(void) DECLSPEC_HIDDEN; /* Detect Shell Links */ BOOL SHELL_IsShortcut(LPCITEMIDLIST) DECLSPEC_HIDDEN; -INT_PTR CALLBACK SH_FileGeneralDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); -INT_PTR CALLBACK SH_FileVersionDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); HPROPSHEETPAGE SH_CreatePropertySheetPage(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, LPCWSTR pwszTitle); -BOOL SH_ShowDriveProperties(WCHAR *drive, IDataObject *pDataObj); -BOOL SH_ShowRecycleBinProperties(WCHAR sDrive); -BOOL SH_ShowPropertiesDialog(LPCWSTR pwszPath, IDataObject *pDataObj); +HPROPSHEETPAGE SH_CreatePropertySheetPageEx(WORD wDialogId, DLGPROC pfnDlgProc, LPARAM lParam, + LPCWSTR pwszTitle, LPFNPSPCALLBACK Callback); LPWSTR SH_FormatFileSizeWithBytes(PULARGE_INTEGER lpQwSize, LPWSTR pszBuf, UINT cchBuf); HRESULT WINAPI DoRegisterServer(void);