[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.
This commit is contained in:
Whindmar Saksit 2024-11-27 17:45:03 +01:00 committed by GitHub
parent 53f498c968
commit 2aadf2eb26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 577 additions and 596 deletions

View 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)

View 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

View 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)

View 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)
{

View 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)
{

View 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;

View File

@ -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 */

View File

@ -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());
}

View 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);
}

View 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)
{

View 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)
{

View 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)

View 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
{

View 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()
};

View File

@ -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);
}

View 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)

View 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);

View 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;
}

View 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))
CDataObjectHIDA cida(pdtobj);
if (FAILED_UNEXPECTEDLY(cida.hr()))
return cida.hr();
if (cida->cidl > 1)
{
pdtobj->Release();
return HResultFromWin32(GetLastError());
}
else
{
return S_OK;
ERR("SHMultiFileProperties is not yet implemented\n");
return E_FAIL;
}
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
}
HRESULT

View 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
*/

View 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,

View 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);