[SHELL32] Multiple drives property sheet support (#7787)

Supports multiple drives in a single property sheet

CORE-20025
This commit is contained in:
Whindmar Saksit 2025-03-20 13:33:51 +01:00 committed by GitHub
parent d769f5675d
commit 7f49ae633b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 163 additions and 53 deletions

View file

@ -689,11 +689,39 @@ CDrvDefExt::~CDrvDefExt()
}
struct CDrop
{
HRESULT hr;
STGMEDIUM stgm;
HDROP hDrop;
explicit CDrop(IDataObject *pDO)
{
FORMATETC format = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
hDrop = SUCCEEDED(hr = pDO->GetData(&format, &stgm)) ? (HDROP)stgm.hGlobal : NULL;
}
~CDrop()
{
if (hDrop)
ReleaseStgMedium(&stgm);
}
UINT GetCount()
{
return DragQueryFileW(hDrop, -1, NULL, 0);
}
};
static inline bool
IsValidDrivePath(PCWSTR Path)
{
return GetDriveTypeW(Path) > DRIVE_NO_ROOT_DIR;
}
HRESULT WINAPI
CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY hkeyProgID)
{
FORMATETC format;
STGMEDIUM stgm;
HRESULT hr;
TRACE("%p %p %p %p\n", this, pidlFolder, pDataObj, hkeyProgID);
@ -701,24 +729,20 @@ CDrvDefExt::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject *pDataObj, HKEY
if (!pDataObj)
return E_FAIL;
format.cfFormat = CF_HDROP;
format.ptd = NULL;
format.dwAspect = DVASPECT_CONTENT;
format.lindex = -1;
format.tymed = TYMED_HGLOBAL;
hr = pDataObj->GetData(&format, &stgm);
if (FAILED(hr))
CDrop drop(pDataObj);
if (FAILED_UNEXPECTEDLY(hr = drop.hr))
return hr;
if (!DragQueryFileW((HDROP)stgm.hGlobal, 0, m_wszDrive, _countof(m_wszDrive)))
if (!DragQueryFileW(drop.hDrop, 0, m_wszDrive, _countof(m_wszDrive)))
{
ERR("DragQueryFileW failed\n");
ReleaseStgMedium(&stgm);
return E_FAIL;
}
ReleaseStgMedium(&stgm);
if (drop.GetCount() > 1)
m_Multiple = pDataObj;
TRACE("Drive properties %ls\n", m_wszDrive);
return S_OK;
@ -745,37 +769,75 @@ CDrvDefExt::GetCommandString(UINT_PTR idCmd, UINT uType, UINT *pwReserved, LPSTR
return E_NOTIMPL;
}
HRESULT WINAPI
CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
HRESULT
CDrvDefExt::AddMainPage(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
{
HPROPSHEETPAGE hPage;
WCHAR szTitle[MAX_PATH], *pszTitle = NULL;
if (m_Multiple)
{
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidl(SHSimpleIDListFromPathW(m_wszDrive));
if (SUCCEEDED(SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, szTitle, _countof(szTitle), NULL)))
pszTitle = szTitle;
}
HPROPSHEETPAGE hPage;
hPage = SH_CreatePropertySheetPageEx(IDD_DRIVE_PROPERTIES, GeneralPageProc, (LPARAM)this,
NULL, &PropSheetPageLifetimeCallback<CDrvDefExt>);
pszTitle, &PropSheetPageLifetimeCallback<CDrvDefExt>);
HRESULT hr = AddPropSheetPage(hPage, pfnAddPage, lParam);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
else
AddRef(); // For PropSheetPageLifetimeCallback
return hr;
}
if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
HRESULT WINAPI
CDrvDefExt::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
{
HRESULT hr = AddMainPage(pfnAddPage, lParam);
if (FAILED_UNEXPECTEDLY(hr))
return hr;
if (m_Multiple)
{
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
ExtraPageProc,
(LPARAM)this,
NULL);
if (hPage)
pfnAddPage(hPage, lParam);
CDrop drop(m_Multiple);
UINT count = SUCCEEDED(drop.hr) ? drop.GetCount() : 0;
for (UINT i = 0; ++i < count;) // Skipping the first drive since it already has a page
{
CComPtr<CDrvDefExt> SheetExt;
if (FAILED_UNEXPECTEDLY(hr = ShellObjectCreator(SheetExt)))
continue;
if (!DragQueryFileW(drop.hDrop, i, SheetExt->m_wszDrive, _countof(SheetExt->m_wszDrive)))
continue;
if (!IsValidDrivePath(SheetExt->m_wszDrive))
continue;
SheetExt->m_Multiple = m_Multiple;
SheetExt->AddMainPage(pfnAddPage, lParam);
}
}
if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
else
{
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
HardwarePageProc,
(LPARAM)this,
NULL);
if (hPage)
pfnAddPage(hPage, lParam);
HPROPSHEETPAGE hPage;
if (GetDriveTypeW(m_wszDrive) == DRIVE_FIXED)
{
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_TOOLS,
ExtraPageProc,
(LPARAM)this,
NULL);
if (hPage)
pfnAddPage(hPage, lParam);
}
if (GetDriveTypeW(m_wszDrive) != DRIVE_REMOTE)
{
hPage = SH_CreatePropertySheetPage(IDD_DRIVE_HARDWARE,
HardwarePageProc,
(LPARAM)this,
NULL);
if (hPage)
pfnAddPage(hPage, lParam);
}
}
return S_OK;

View file

@ -38,6 +38,9 @@ private:
WCHAR m_wszDrive[MAX_PATH];
UINT m_FreeSpacePerc;
CComPtr<IDataObject> m_Multiple;
HRESULT AddMainPage(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam);
public:
CDrvDefExt();

View file

@ -17,6 +17,9 @@ SHELL_GetCaptionFromDataObject(IDataObject *pDO, LPWSTR Buf, UINT cchBuf)
{
hr = SHGetNameAndFlagsW(pidl, SHGDN_INFOLDER, Buf, cchBuf, NULL);
ILFree(pidl);
if (SUCCEEDED(hr) && DataObject_GetHIDACount(pDO) > 1)
StringCchCatW(Buf, cchBuf, L", ...");
}
return hr;
}
@ -130,7 +133,15 @@ FSFolderItemPropDialogInitCallback(LPCWSTR InitString, IDataObject *pDO, HKEY *h
CDataObjectHIDA cida(pDO);
if (SUCCEEDED(cida.hr()) && cida->cidl)
{
#if 0
CCidaChildArrayHelper items(cida);
if (FAILED(hr = items.hr()))
return hr;
#else
// Note: Since we are only passing a single item to AddFSClassKeysToArray,
// we don't need the rest of the array to be valid.
PCUITEMID_CHILD pidl = HIDA_GetPIDLItem(cida, 0);
#endif
AddFSClassKeysToArray(1, &pidl, hKeys, cKeys); // Add file-type specific pages
}
}
@ -154,7 +165,7 @@ SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
ShellPropSheetDialog::PFNINITIALIZE InitFunc = NULL;
LPCWSTR InitString = NULL;
if (_ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
if (cida->cidl && _ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
{
pClsid = &CLSID_ShellDrvDefExt;
InitFunc = ClassPropDialogInitCallback;
@ -169,6 +180,18 @@ SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO)
return Dialog.ShowAsync(pClsid, pDO, InitFunc, InitString);
}
HRESULT
SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO)
{
if (DataObject_GetHIDACount(pDO) == 1)
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDO);
ERR("SHMultiFileProperties is not implemented yet\n");
HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
SHELL_ErrorBox(hOwner, hr);
return hr; // TODO: return SHMultiFileProperties(pDO, 0);
}
HRESULT
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
{

View file

@ -172,6 +172,8 @@ SHELL32_ShowPropertiesDialog(IDataObject *pdtobj);
HRESULT
SHELL32_ShowFilesystemItemPropertiesDialogAsync(IDataObject *pDO);
HRESULT
SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO);
HRESULT
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO);
HRESULT
SHELL_ShowItemIDListProperties(LPCITEMIDLIST pidl);
@ -296,6 +298,7 @@ BOOL PathIsDosDevice(_In_ LPCWSTR pszName);
HRESULT SHELL32_GetDllFromRundll32CommandLine(LPCWSTR pszCmd, LPWSTR pszOut, SIZE_T cchMax);
HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl);
HRESULT DataObject_GetHIDACount(IDataObject *pdo);
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index);
PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UINT Index);
HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO);

View file

@ -98,6 +98,15 @@ HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject* pDataObject, DWORD dwA
return hr;
}
HRESULT DataObject_GetHIDACount(IDataObject *pdo)
{
if (!pdo)
return E_INVALIDARG;
CDataObjectHIDA cida(pdo);
HRESULT hr = cida.hr();
return SUCCEEDED(hr) ? cida->cidl : hr;
}
PIDLIST_ABSOLUTE SHELL_CIDA_ILCloneFull(_In_ const CIDA *pCIDA, _In_ UINT Index)
{
if (Index < pCIDA->cidl)
@ -120,8 +129,10 @@ HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO)
HRESULT hr = cida.hr();
if (SUCCEEDED(hr))
{
PCUITEMID_CHILD items = HIDA_GetPIDLItem(cida, 0);
hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, &items, NULL, ppDO);
CCidaChildArrayHelper items(cida);
if (FAILED(hr = items.hr()))
return hr;
hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, items.GetItems(), NULL, ppDO);
if (SUCCEEDED(hr))
{
POINT pt;

View file

@ -509,16 +509,7 @@ SHELL32_ShowPropertiesDialog(IDataObject *pdtobj)
{
if (!pdtobj)
return E_INVALIDARG;
CDataObjectHIDA cida(pdtobj);
if (FAILED_UNEXPECTEDLY(cida.hr()))
return cida.hr();
if (cida->cidl > 1)
{
ERR("SHMultiFileProperties is not yet implemented\n");
return E_FAIL;
}
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pdtobj);
return SHELL32_ShowFilesystemItemsPropertiesDialogAsync(NULL, pdtobj);
}
HRESULT

View file

@ -84,15 +84,6 @@ SHParseDarwinIDFromCacheW(LPCWSTR lpUnknown1, LPWSTR lpUnknown2)
return E_FAIL;
}
static HRESULT DataObject_GetHIDACount(IDataObject *pdo)
{
if (!pdo)
return E_INVALIDARG;
CDataObjectHIDA cida(pdo);
HRESULT hr = cida.hr();
return SUCCEEDED(hr) ? cida->cidl : hr;
}
/*
* Unimplemented
*/

View file

@ -91,6 +91,7 @@ SHELL_CreateFallbackExtractIconForNoAssocFile(REFIID riid, LPVOID *ppvOut)
return SHELL_CreateShell32DefaultExtractIcon(id > 1 ? -id : 0, riid, ppvOut);
}
#ifdef __cplusplus
struct ClipboardViewerChain
{
HWND m_hWndNext = HWND_BOTTOM;
@ -124,3 +125,28 @@ struct ClipboardViewerChain
return 0;
}
};
struct CCidaChildArrayHelper
{
// Note: This just creates an array pointing to the items and has the same lifetime as the CIDA.
// Use _ILCopyCidaToaPidl if you need the items to outlive the CIDA!
explicit CCidaChildArrayHelper(const CIDA *pCida)
{
m_hr = E_OUTOFMEMORY;
m_array = (PCUIDLIST_RELATIVE_ARRAY)SHAlloc(pCida->cidl * sizeof(LPITEMIDLIST));
if (m_array)
{
m_hr = S_OK;
for (UINT i = 0; i < pCida->cidl; ++i)
*(LPITEMIDLIST*)(&m_array[i]) = (LPITEMIDLIST)HIDA_GetPIDLItem(pCida, i);
}
}
~CCidaChildArrayHelper() { SHFree((LPITEMIDLIST*)m_array); }
HRESULT hr() const { return m_hr; }
PCUIDLIST_RELATIVE_ARRAY GetItems() const { return m_array; }
HRESULT m_hr;
PCUIDLIST_RELATIVE_ARRAY m_array;
};
#endif // __cplusplus