[SHELL32] Implement and use SHOpenPropSheet (#7432)
authorWhindmar Saksit <whindsaks@proton.me>
Wed, 27 Nov 2024 16:45:03 +0000 (17:45 +0100)
committerGitHub <noreply@github.com>
Wed, 27 Nov 2024 16:45:03 +0000 (17:45 +0100)
- Implement SHOpenPropSheetW.
- Reuse already opened propertysheets and format dialogs by finding the existing unique stub window.
- Default .lnk property dialog to the shortcut tab.

22 files changed:
dll/win32/shell32/CFolderOptions.cpp
dll/win32/shell32/CMakeLists.txt
dll/win32/shell32/CShellLink.cpp
dll/win32/shell32/dialogs/drive.cpp
dll/win32/shell32/dialogs/drvdefext.cpp
dll/win32/shell32/dialogs/filedefext.cpp
dll/win32/shell32/dialogs/fprop.cpp [deleted file]
dll/win32/shell32/dialogs/item_prop.cpp [new file with mode: 0644]
dll/win32/shell32/dialogs/recycler_prop.cpp
dll/win32/shell32/folders/CDrivesFolder.cpp
dll/win32/shell32/folders/CFSFolder.cpp
dll/win32/shell32/folders/CRecycleBin.cpp
dll/win32/shell32/folders/CRegFolder.cpp
dll/win32/shell32/precomp.h
dll/win32/shell32/propsheet.cpp [new file with mode: 0644]
dll/win32/shell32/shell32.cpp
dll/win32/shell32/shfldr.h
dll/win32/shell32/shldataobject.cpp
dll/win32/shell32/shlfolder.cpp
dll/win32/shell32/stubs.cpp
dll/win32/shell32/utils.cpp
dll/win32/shell32/wine/shell32_main.h

index ba436c9..0f3513b 100644 (file)
@@ -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)
index d7f7672..2581913 100644 (file)
@@ -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
index 582a605..d99db2f 100644 (file)
@@ -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)
index 8bade5d..ff97054 100644 (file)
@@ -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)
 {
index c11375e..f26413b 100644 (file)
@@ -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)
     {
index dd3c5cd..e8beb7f 100644 (file)
@@ -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 (file)
index 68872bc..0000000
+++ /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 (file)
index 0000000..90160a8
--- /dev/null
@@ -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@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());
+}
index ea1c9ca..d7f7925 100644 (file)
@@ -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);
 }
index fd41deb..040511f 100644 (file)
@@ -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)
             {
index bb73ec8..9ae1f53 100644 (file)
@@ -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)
         {
index 78b3d03..a9660e2 100644 (file)
@@ -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)
index 510727a..b983b54 100644 (file)
@@ -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
     {
index 614478a..a28c023 100644 (file)
@@ -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 (file)
index 0000000..3fccaa0
--- /dev/null
@@ -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@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-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);
+}
index 3d55c35..a43afe2 100644 (file)
@@ -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)
index ff39712..b9cd401 100644 (file)
@@ -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);
index fb50c78..3a7d651 100644 (file)
@@ -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;
 }
index 37658ce..4e08e19 100644 (file)
@@ -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
index 6b6e141..77e674e 100644 (file)
@@ -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
  */
index 3fdce2e..ce2f6f4 100644 (file)
@@ -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,
index 4392a50..d40e1a1 100644 (file)
@@ -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);