From 03ee0756b872a8a487876a97e5152fffbd524c73 Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Wed, 8 Jan 2025 12:22:34 +0100 Subject: [PATCH] [SHELL32] Clone properties IDataObject for unlimited lifetime (#7601) Because SHOpenPropSheetW() is modal, there is no way for us to keep COM alive on the original thread until the dialog completes, so a cloned data object is used instead so that the property sheet handlers may access the object even after ShellExecute() has returned (this is only relevant for callers like Taskmgr that does not initialize COM even though MSDN says they should). Addendum to afc130ff3f5 (PR #7571). CORE-19933 --- dll/win32/shell32/dialogs/item_prop.cpp | 15 +++++++++------ dll/win32/shell32/precomp.h | 1 + dll/win32/shell32/shldataobject.cpp | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/dll/win32/shell32/dialogs/item_prop.cpp b/dll/win32/shell32/dialogs/item_prop.cpp index bcc6473144d1c..75039293c3c20 100644 --- a/dll/win32/shell32/dialogs/item_prop.cpp +++ b/dll/win32/shell32/dialogs/item_prop.cpp @@ -27,7 +27,7 @@ struct ShellPropSheetDialog HKEY *hKeys, UINT *cKeys); static HRESULT Show(const CLSID *pClsidDefault, IDataObject *pDO, - PFNINITIALIZE InitFunc, LPCWSTR InitString, HANDLE hEvent = NULL) + PFNINITIALIZE InitFunc, LPCWSTR InitString) { HRESULT hr; CRegKeyHandleArray keys; @@ -36,8 +36,6 @@ struct ShellPropSheetDialog WCHAR szCaption[MAX_PATH], *pszCaption = NULL; if (SUCCEEDED(SHELL_GetCaptionFromDataObject(pDO, szCaption, _countof(szCaption)))) pszCaption = szCaption; - if (hEvent) - SetEvent(hEvent); hr = SHOpenPropSheetW(pszCaption, keys, keys, pClsidDefault, pDO, NULL, NULL) ? S_OK : E_FAIL; return hr; } @@ -96,7 +94,8 @@ struct ShellPropSheetDialog if (hEvent) { DWORD index; - // Pump COM messages until InitFunc is done (for CORE-19933) + // 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); } @@ -111,10 +110,14 @@ struct ShellPropSheetDialog static DWORD CALLBACK ShowPropertiesThread(LPVOID Param) { DATA *pData = (DATA*)Param; - CComPtr pDO; + CComPtr pDO, pLocalDO; if (pData->pObjStream) CoGetInterfaceAndReleaseStream(pData->pObjStream, IID_PPV_ARG(IDataObject, &pDO)); - Show(pData->pClsidDefault, pDO, pData->InitFunc, pData->InitString, pData->hEvent); + 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; } diff --git a/dll/win32/shell32/precomp.h b/dll/win32/shell32/precomp.h index 48c636fe9f1b3..6d65aeac0560f 100644 --- a/dll/win32/shell32/precomp.h +++ b/dll/win32/shell32/precomp.h @@ -297,6 +297,7 @@ HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl); 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); EXTERN_C HRESULT IUnknown_InitializeCommand( diff --git a/dll/win32/shell32/shldataobject.cpp b/dll/win32/shell32/shldataobject.cpp index 3a7d651424f0a..728cca0dbf376 100644 --- a/dll/win32/shell32/shldataobject.cpp +++ b/dll/win32/shell32/shldataobject.cpp @@ -111,3 +111,22 @@ PIDLIST_ABSOLUTE SHELL_DataObject_ILCloneFullItem(_In_ IDataObject *pDO, _In_ UI CDataObjectHIDA cida(pDO); return SUCCEEDED(cida.hr()) ? SHELL_CIDA_ILCloneFull(cida, Index) : NULL; } + +HRESULT SHELL_CloneDataObject(_In_ IDataObject *pDO, _Out_ IDataObject **ppDO) +{ + *ppDO = NULL; + CDataObjectHIDA cida(pDO); + HRESULT hr = cida.hr(); + if (SUCCEEDED(hr)) + { + PCUITEMID_CHILD items = HIDA_GetPIDLItem(cida, 0); + hr = SHCreateFileDataObject(HIDA_GetPIDLFolder(cida), cida->cidl, &items, NULL, ppDO); + if (SUCCEEDED(hr)) + { + POINT pt; + if (SUCCEEDED(DataObject_GetOffset(pDO, &pt))) + DataObject_SetOffset(*ppDO, &pt); + } + } + return hr; +}