From 53518bbab3801959d3effe8562b5d14e8d26e2b9 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Thu, 23 May 2024 22:57:31 +0900 Subject: [PATCH] [SHLWAPI][SHLWAPI_APITEST][SDK] Implement IShellFolder_GetDisplayNameOf (#6918) This export function is needed to implement shell32!SHGetRealIDL function correctly. JIRA issue: CORE-19278 - Implement IShellFolder_GetDisplayNameOf function (This function is not inline function in this case) with retry data. - Add SFGDNO_RETRYALWAYS flag to . - Add IShellFolderHelpers testcase. --- dll/win32/shlwapi/utils.cpp | 51 ++- .../rostests/apitests/shlwapi/CMakeLists.txt | 1 + .../apitests/shlwapi/IShellFolderHelpers.cpp | 308 ++++++++++++++++++ modules/rostests/apitests/shlwapi/testlist.c | 2 + sdk/include/reactos/shlwapi_undoc.h | 5 +- 5 files changed, 360 insertions(+), 7 deletions(-) create mode 100644 modules/rostests/apitests/shlwapi/IShellFolderHelpers.cpp diff --git a/dll/win32/shlwapi/utils.cpp b/dll/win32/shlwapi/utils.cpp index 3a5db3516ac..a452b3f7253 100644 --- a/dll/win32/shlwapi/utils.cpp +++ b/dll/win32/shlwapi/utils.cpp @@ -140,16 +140,36 @@ SHLWAPI_IsBogusHRESULT(HRESULT hr) return (hr == E_FAIL || hr == E_INVALIDARG || hr == E_NOTIMPL); } +// Used for IShellFolder_GetDisplayNameOf +struct RETRY_DATA +{ + SHGDNF uRemove; + SHGDNF uAdd; + DWORD dwRetryFlags; +}; +static const RETRY_DATA g_RetryData[] = +{ + { SHGDN_FOREDITING, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, + { SHGDN_FORADDRESSBAR, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, + { SHGDN_NORMAL, SHGDN_FORPARSING, SFGDNO_RETRYALWAYS }, + { SHGDN_FORPARSING, SHGDN_NORMAL, SFGDNO_RETRYWITHFORPARSING }, + { SHGDN_INFOLDER, SHGDN_NORMAL, SFGDNO_RETRYALWAYS }, +}; + /************************************************************************* * IShellFolder_GetDisplayNameOf [SHLWAPI.316] + * + * @note Don't confuse with inline function of the same name. + * If the original call fails with the given uFlags, this function will + * retry with other flags to attempt retrieving any meaningful description. */ EXTERN_C HRESULT WINAPI IShellFolder_GetDisplayNameOf( _In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, - _In_ DWORD uFlags, + _In_ SHGDNF uFlags, _Out_ LPSTRRET lpName, - _In_ DWORD dwRetryFlags) + _In_ DWORD dwRetryFlags) // dwRetryFlags is an additional parameter { HRESULT hr; @@ -159,19 +179,37 @@ IShellFolder_GetDisplayNameOf( if (!SHLWAPI_IsBogusHRESULT(hr)) return hr; - dwRetryFlags |= 0x80000000; + dwRetryFlags |= SFGDNO_RETRYALWAYS; if ((uFlags & SHGDN_FORPARSING) == 0) dwRetryFlags |= SFGDNO_RETRYWITHFORPARSING; - /* It seems the function is actually retrying here */ - FIXME("dwRetryFlags: 0x%X\n", dwRetryFlags); + // Retry with other flags to get successful results + for (SIZE_T iEntry = 0; iEntry < _countof(g_RetryData); ++iEntry) + { + const RETRY_DATA *pData = &g_RetryData[iEntry]; + if (!(dwRetryFlags & pData->dwRetryFlags)) + continue; + + SHGDNF uNewFlags = ((uFlags & ~pData->uRemove) | pData->uAdd); + if (uNewFlags == uFlags) + continue; + + hr = psf->GetDisplayNameOf(pidl, uNewFlags, lpName); + if (!SHLWAPI_IsBogusHRESULT(hr)) + break; + + uFlags = uNewFlags; // Update flags every time + } return hr; } /************************************************************************* * IShellFolder_ParseDisplayName [SHLWAPI.317] + * + * @note Don't confuse with inline function of the same name. + * This function is safer than IShellFolder::ParseDisplayName. */ EXTERN_C HRESULT WINAPI IShellFolder_ParseDisplayName( @@ -209,6 +247,9 @@ IShellFolder_ParseDisplayName( /************************************************************************* * IShellFolder_CompareIDs [SHLWAPI.551] + * + * @note Don't confuse with inline function of the same name. + * This function tries IShellFolder2 if possible. */ EXTERN_C HRESULT WINAPI IShellFolder_CompareIDs( diff --git a/modules/rostests/apitests/shlwapi/CMakeLists.txt b/modules/rostests/apitests/shlwapi/CMakeLists.txt index 95271b056ae..49e5ef69056 100644 --- a/modules/rostests/apitests/shlwapi/CMakeLists.txt +++ b/modules/rostests/apitests/shlwapi/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories($) list(APPEND SOURCE AssocQueryString.c + IShellFolderHelpers.cpp PathFileExistsDefExtAndAttributesW.c PathFindOnPath.c PathIsUNC.c diff --git a/modules/rostests/apitests/shlwapi/IShellFolderHelpers.cpp b/modules/rostests/apitests/shlwapi/IShellFolderHelpers.cpp new file mode 100644 index 00000000000..588ef3f329c --- /dev/null +++ b/modules/rostests/apitests/shlwapi/IShellFolderHelpers.cpp @@ -0,0 +1,308 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for SHLWAPI IShellFolder helpers + * COPYRIGHT: Copyright 2024 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include + +#define SHLWAPI_ISHELLFOLDER_HELPERS +#include + +static INT s_nStep = 0; + +class CTestShellFolder : public IShellFolder +{ +public: + CTestShellFolder() { } + virtual ~CTestShellFolder() { } + + static void *operator new(size_t size) + { + return LocalAlloc(LPTR, size); + } + static void operator delete(void *ptr) + { + LocalFree(ptr); + } + static void operator delete(void *ptr, size_t size) + { + LocalFree(ptr); + } + + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject) override + { + ok_int(s_nStep, 11); + ok_int(IsEqualGUID(riid, IID_IShellFolder2), TRUE); + ++s_nStep; + return E_NOINTERFACE; + } + STDMETHOD_(ULONG, AddRef)() override + { + ok_int(TRUE, FALSE); + return 1; + } + STDMETHOD_(ULONG, Release)() override + { + ok_int(TRUE, FALSE); + return 1; + } + + // IShellFolder methods + STDMETHOD(ParseDisplayName)( + HWND hwndOwner, + LPBC pbc, + LPOLESTR lpszDisplayName, + ULONG *pchEaten, + PIDLIST_RELATIVE *ppidl, + ULONG *pdwAttributes) override + { + ok_ptr(*ppidl, NULL); + ok_long(*pdwAttributes, 0); + ++s_nStep; + return 0xDEADFACE; + } + STDMETHOD(EnumObjects)( + HWND hwndOwner, + DWORD dwFlags, + LPENUMIDLIST *ppEnumIDList) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(BindToObject)( + PCUIDLIST_RELATIVE pidl, + LPBC pbcReserved, + REFIID riid, + LPVOID *ppvOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(BindToStorage)( + PCUIDLIST_RELATIVE pidl, + LPBC pbcReserved, + REFIID riid, + LPVOID *ppvOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(CompareIDs)( + LPARAM lParam, + PCUIDLIST_RELATIVE pidl1, + PCUIDLIST_RELATIVE pidl2) override + { + switch (s_nStep) + { + case 11: + // It shouldn't come here + ok_int(TRUE, FALSE); + break; + case 12: + ok_long((LONG)lParam, 0x00001234); + break; + case 13: + ok_long((LONG)lParam, 0x00005678); + break; + default: + skip("\n"); + break; + } + ++s_nStep; + return 0xFEEDF00D; + } + STDMETHOD(CreateViewObject)( + HWND hwndOwner, + REFIID riid, + LPVOID *ppvOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(GetAttributesOf)( + UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + DWORD *rgfInOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(GetUIObjectOf)( + HWND hwndOwner, + UINT cidl, + PCUITEMID_CHILD_ARRAY apidl, + REFIID riid, + UINT * prgfInOut, + LPVOID * ppvOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } + STDMETHOD(GetDisplayNameOf)( + PCUITEMID_CHILD pidl, + DWORD dwFlags, + LPSTRRET strRet) override + { + switch (s_nStep) + { + case 0: + ok_long(dwFlags, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR | SHGDN_FOREDITING | + SHGDN_INFOLDER); + break; + case 1: + ok_long(dwFlags, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR | SHGDN_INFOLDER); + break; + case 2: + ok_long(dwFlags, SHGDN_FORPARSING | SHGDN_INFOLDER); + break; + case 3: + ok_long(dwFlags, SHGDN_FORPARSING); + break; + case 4: + ok_long(dwFlags, SHGDN_FORADDRESSBAR | SHGDN_FOREDITING | SHGDN_INFOLDER); + break; + case 5: + ok_long(dwFlags, SHGDN_FORADDRESSBAR | SHGDN_INFOLDER); + break; + case 6: + ok_long(dwFlags, SHGDN_INFOLDER); + break; + case 7: + ok_long(dwFlags, SHGDN_FORPARSING | SHGDN_INFOLDER); + break; + case 8: + ok_long(dwFlags, SHGDN_INFOLDER); + break; + case 9: + ok_long(dwFlags, SHGDN_NORMAL); + break; + default: + skip("\n"); + break; + } + ++s_nStep; + return E_FAIL; + } + STDMETHOD(SetNameOf)( + HWND hwndOwner, + PCUITEMID_CHILD pidl, + LPCOLESTR lpName, + DWORD dwFlags, + PITEMID_CHILD *pPidlOut) override + { + ok_int(TRUE, FALSE); + return E_NOTIMPL; + } +}; + +static void Test_GetDisplayNameOf(void) +{ + CTestShellFolder *psf = new CTestShellFolder(); + HRESULT hr; + + hr = IShellFolder_GetDisplayNameOf( + psf, + NULL, + SHGDN_FOREDITING | SHGDN_FORADDRESSBAR | SHGDN_FORPARSING | SHGDN_INFOLDER, + NULL, + 0); + ok_long(hr, E_FAIL); + ok_int(s_nStep, 4); + + hr = IShellFolder_GetDisplayNameOf( + psf, + NULL, + SHGDN_FOREDITING | SHGDN_FORADDRESSBAR | SHGDN_INFOLDER, + NULL, + 0); + ok_long(hr, E_FAIL); + ok_int(s_nStep, 10); + + if (s_nStep != 10) + skip("s_nStep value is wrong\n"); + + delete psf; +} + +static void Test_ParseDisplayName(void) +{ + CTestShellFolder *psf = new CTestShellFolder(); + HRESULT hr; + + s_nStep = 10; + LPITEMIDLIST pidl = (LPITEMIDLIST)UlongToPtr(0xDEADDEAD); + hr = IShellFolder_ParseDisplayName( + psf, + NULL, + NULL, + NULL, + NULL, + &pidl, + NULL); + ok_long(hr, 0xDEADFACE); + ok_int(s_nStep, 11); + + delete psf; +} + +typedef HRESULT (WINAPI *FN_IShellFolder_CompareIDs)( + _In_ IShellFolder *psf, + _In_ LPARAM lParam, + _In_ PCUIDLIST_RELATIVE pidl1, + _In_ PCUIDLIST_RELATIVE pidl2); + +static void Test_CompareIDs(void) +{ + FN_IShellFolder_CompareIDs fnIShellFolder_CompareIDs; + fnIShellFolder_CompareIDs = + (FN_IShellFolder_CompareIDs) + GetProcAddress(GetModuleHandleA("shlwapi"), MAKEINTRESOURCEA(551)); + + if (IsWindowsVistaOrGreater()) + { + skip("Vista+\n"); + ok(fnIShellFolder_CompareIDs == NULL, "Vista+ has no IShellFolder_CompareIDs\n"); + return; + } + + CTestShellFolder *psf = new CTestShellFolder(); + HRESULT hr; + + s_nStep = 11; + hr = fnIShellFolder_CompareIDs( + psf, + 0xFFFF1234, + NULL, + NULL); + ok_long(hr, 0xFEEDF00D); + ok_int(s_nStep, 13); + + s_nStep = 13; + hr = fnIShellFolder_CompareIDs( + psf, + 0x00005678, + NULL, + NULL); + ok_long(hr, 0xFEEDF00D); + ok_int(s_nStep, 14); + + delete psf; +} + +START_TEST(IShellFolderHelpers) +{ + HRESULT hrCoInit = ::CoInitialize(NULL); + + Test_GetDisplayNameOf(); + Test_ParseDisplayName(); + Test_CompareIDs(); + + if (SUCCEEDED(hrCoInit)) + ::CoUninitialize(); +} diff --git a/modules/rostests/apitests/shlwapi/testlist.c b/modules/rostests/apitests/shlwapi/testlist.c index 9bb74d7ee34..b44f2e181fa 100644 --- a/modules/rostests/apitests/shlwapi/testlist.c +++ b/modules/rostests/apitests/shlwapi/testlist.c @@ -4,6 +4,7 @@ extern void func_AssocQueryString(void); extern void func_PathFileExistsDefExtAndAttributesW(void); extern void func_PathFindOnPath(void); +extern void func_IShellFolderHelpers(void); extern void func_isuncpath(void); extern void func_isuncpathserver(void); extern void func_isuncpathservershare(void); @@ -22,6 +23,7 @@ const struct test winetest_testlist[] = { "AssocQueryString", func_AssocQueryString }, { "PathFileExistsDefExtAndAttributesW", func_PathFileExistsDefExtAndAttributesW }, { "PathFindOnPath", func_PathFindOnPath }, + { "IShellFolderHelpers", func_IShellFolderHelpers }, { "PathIsUNC", func_isuncpath }, { "PathIsUNCServer", func_isuncpathserver }, { "PathIsUNCServerShare", func_isuncpathservershare }, diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index eb002d4a170..12b167e7897 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -341,12 +341,13 @@ HRESULT WINAPI IShellFolder_GetDisplayNameOf( _In_ IShellFolder *psf, _In_ LPCITEMIDLIST pidl, - _In_ DWORD uFlags, + _In_ SHGDNF uFlags, _Out_ LPSTRRET lpName, _In_ DWORD dwRetryFlags); /* Flags for IShellFolder_GetDisplayNameOf */ -#define SFGDNO_RETRYWITHFORPARSING 1 +#define SFGDNO_RETRYWITHFORPARSING 0x00000001 +#define SFGDNO_RETRYALWAYS 0x80000000 HRESULT WINAPI IShellFolder_ParseDisplayName(