reactos/dll/win32/shell32/dialogs/item_prop.cpp
2025-06-08 20:04:10 +02:00

229 lines
7.1 KiB
C++

/*
* 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);
if (SUCCEEDED(hr) && DataObject_GetHIDACount(pDO) > 1)
StringCchCatW(Buf, cchBuf, L", ...");
}
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;
HANDLE hEvent;
};
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;
}
HANDLE hEvent = pData->hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
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 (SUCCEEDED(hr))
{
if (hEvent)
{
DWORD index;
// Pump COM messages until the thread can create its own IDataObject (for CORE-19933).
// SHOpenPropSheetW is modal and we cannot wait for it to complete.
CoWaitForMultipleHandles(COWAIT_DEFAULT, INFINITE, 1, &hEvent, &index);
CloseHandle(hEvent);
}
}
else
{
FreeData(pData);
}
return hr;
}
static DWORD CALLBACK ShowPropertiesThread(LPVOID Param)
{
DATA *pData = (DATA*)Param;
CComPtr<IDataObject> pDO, pLocalDO;
if (pData->pObjStream)
CoGetInterfaceAndReleaseStream(pData->pObjStream, IID_PPV_ARG(IDataObject, &pDO));
if (pDO && SUCCEEDED(SHELL_CloneDataObject(pDO, &pLocalDO)))
pDO = pLocalDO;
if (pData->hEvent)
SetEvent(pData->hEvent);
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)
{
#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
}
}
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 (cida->cidl && _ILIsDrive(HIDA_GetPIDLItem(cida, 0)))
{
pClsid = &CLSID_ShellDrvDefExt;
InitFunc = ClassPropDialogInitCallback;
InitString = L"Drive";
}
else
{
pClsid = &CLSID_ShellFileDefExt;
InitFunc = FSFolderItemPropDialogInitCallback;
}
return ShellPropSheetDialog().ShowAsync(pClsid, pDO, InitFunc, InitString);
}
HRESULT
SHELL32_ShowFilesystemItemsPropertiesDialogAsync(HWND hOwner, IDataObject *pDO)
{
if (DataObject_GetHIDACount(pDO) == 1)
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDO);
return SHMultiFileProperties(pDO, 0);
}
HRESULT
SHELL32_ShowShellExtensionProperties(const CLSID *pClsid, IDataObject *pDO)
{
WCHAR ClassBuf[6 + 38 + 1] = L"CLSID\\";
StringFromGUID2(*pClsid, ClassBuf + 6, 38 + 1);
return ShellPropSheetDialog().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());
}
/*
* SHMultiFileProperties [SHELL32.716]
*/
EXTERN_C HRESULT
WINAPI
SHMultiFileProperties(IDataObject *pDataObject, DWORD dwFlags)
{
if (DataObject_GetHIDACount(pDataObject) == 1)
return SHELL32_ShowFilesystemItemPropertiesDialogAsync(pDataObject);
return ShellPropSheetDialog().ShowAsync(&CLSID_ShellFileDefExt, pDataObject, NULL, NULL);
}