https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2aadf2eb264c537e134fa…
commit 2aadf2eb264c537e134fade391c5a4fb333a8e4d
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Wed Nov 27 17:45:03 2024 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Wed Nov 27 17:45:03 2024 +0100
[SHELL32] Implement and use SHOpenPropSheet (#7432)
- Implement SHOpenPropSheetW.
- Reuse already opened propertysheets and format dialogs by finding the existing
unique stub window.
- Default .lnk property dialog to the shortcut tab.
---
dll/win32/shell32/CFolderOptions.cpp | 14 +-
dll/win32/shell32/CMakeLists.txt | 3 +-
dll/win32/shell32/CShellLink.cpp | 27 ++--
dll/win32/shell32/dialogs/drive.cpp | 122 ----------------
dll/win32/shell32/dialogs/drvdefext.cpp | 13 +-
dll/win32/shell32/dialogs/filedefext.cpp | 47 ++-----
dll/win32/shell32/dialogs/fprop.cpp | 180 ------------------------
dll/win32/shell32/dialogs/item_prop.cpp | 179 +++++++++++++++++++++++
dll/win32/shell32/dialogs/recycler_prop.cpp | 28 +---
dll/win32/shell32/folders/CDrivesFolder.cpp | 128 ++---------------
dll/win32/shell32/folders/CFSFolder.cpp | 20 +--
dll/win32/shell32/folders/CRecycleBin.cpp | 5 +-
dll/win32/shell32/folders/CRegFolder.cpp | 14 +-
dll/win32/shell32/precomp.h | 51 ++++++-
dll/win32/shell32/propsheet.cpp | 211 ++++++++++++++++++++++++++++
dll/win32/shell32/shell32.cpp | 2 +
dll/win32/shell32/shfldr.h | 17 ++-
dll/win32/shell32/shldataobject.cpp | 2 +
dll/win32/shell32/shlfolder.cpp | 51 ++-----
dll/win32/shell32/stubs.cpp | 19 ---
dll/win32/shell32/utils.cpp | 33 +++++
dll/win32/shell32/wine/shell32_main.h | 7 +-
22 files changed, 577 insertions(+), 596 deletions(-)
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<CFolderOptions*>(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<CFolderOptions>);
+ 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<CShellLink>);
+ 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<DRIVE_PROP_DATA, CComAllocator> pPropData((DRIVE_PROP_DATA *)pParam);
- CHeapPtr<WCHAR, CComAllocator> pwszDrive(pPropData->pwszDrive);
-
- // Unmarshall IDataObject from IStream
- CComPtr<IDataObject> pDataObj;
- CoGetInterfaceAndReleaseStream(pPropData->pStream, IID_PPV_ARG(IDataObject,
&pDataObj));
-
- HPSXA hpsx = NULL;
- HPROPSHEETPAGE hpsp[MAX_PROPERTY_SHEET_PAGE];
- CComObject<CDrvDefExt> *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<CDrvDefExt>::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<CDrvDefExt>);
+ 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<CFileDefExt>);
+ 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<CFileDefExt> *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<CFileDefExt>::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 <whindsaks(a)proton.me>
+ */
+
+#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<IDataObject> 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<ITEMIDLIST> alloc;
+ if (IS_INTRESOURCE(pidl))
+ {
+ HRESULT hr = SHGetSpecialFolderLocation(NULL, LOWORD(pidl),
const_cast<LPITEMIDLIST*>(&pidl));
+ if (FAILED(hr))
+ return hr;
+ alloc.Attach(const_cast<LPITEMIDLIST>(pidl));
+ }
+ SHELLEXECUTEINFOA sei = {
+ sizeof(sei), SEE_MASK_INVOKEIDLIST | SEE_MASK_ASYNCOK, NULL,
"properties",
+ NULL, NULL, NULL, SW_SHOWNORMAL, NULL, const_cast<LPITEMIDLIST>(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<HWND> *pStubs = reinterpret_cast<CSimpleArray<HWND>
*>(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<HWND *>(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<HWND> 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<ITEMID_CHILD>
pidlChild(ILClone(ILFindLastID(m_pidlRoot)));
- CComHeapPtr<ITEMIDLIST> pidlParent(ILClone(m_pidlRoot));
- ILRemoveLastID(pidlParent);
-
- CComPtr<IDataObject> 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<WCHAR> 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<CRegFolder*>(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<class T> 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<CStubWindow32>
{
+ 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 <whindsaks(a)proton.me>
+ */
+
+#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<IShellExtInit> 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<IShellPropSheetExt> 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-shobji…
+ 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-shopen…
+ */
+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<IDataObject> 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<ITEMIDLIST> completePidl(ILCombine(HIDA_GetPIDLFolder(cida),
HIDA_GetPIDLItem(cida, 0)));
- CComHeapPtr<WCHAR> 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))
- {
- pdtobj->Release();
- return HResultFromWin32(GetLastError());
- }
- else
+ CDataObjectHIDA cida(pdtobj);
+ if (FAILED_UNEXPECTEDLY(cida.hr()))
+ return cida.hr();
+ if (cida->cidl > 1)
{
- 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-shopen…
- */
-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);