diff --git a/dll/win32/shell32/stubs.cpp b/dll/win32/shell32/stubs.cpp index 91e8ae00beb..118e1c1f670 100644 --- a/dll/win32/shell32/stubs.cpp +++ b/dll/win32/shell32/stubs.cpp @@ -17,14 +17,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); /* * Unimplemented */ -EXTERN_C BOOL -WINAPI -PathIsEqualOrSubFolder(LPWSTR lpFolder, LPWSTR lpSubFolder) -{ - FIXME("PathIsEqualOrSubFolder() stub\n"); - return FALSE; -} - EXTERN_C HRESULT WINAPI SHGetUnreadMailCountW(HKEY hKeyUser, diff --git a/dll/win32/shell32/utils.cpp b/dll/win32/shell32/utils.cpp index 1e7cfbe28ac..9f9309b645c 100644 --- a/dll/win32/shell32/utils.cpp +++ b/dll/win32/shell32/utils.cpp @@ -747,3 +747,119 @@ SHStartNetConnectionDialogA( return SHStartNetConnectionDialogW(hwnd, pszRemoteNameW, dwType); } + +/************************************************************************* + * Helper functions for PathIsEqualOrSubFolder + */ + +static INT +DynamicPathCommonPrefixW( + _In_ LPCWSTR lpszPath1, + _In_ LPCWSTR lpszPath2, + _Out_ CStringW& strPath) +{ + SIZE_T cchPath1 = wcslen(lpszPath1); + SIZE_T cchPath2 = wcslen(lpszPath2); + LPWSTR lpszPath = strPath.GetBuffer((INT)max(cchPath1, cchPath2) + 16); + INT ret = PathCommonPrefixW(lpszPath1, lpszPath2, lpszPath); + strPath.ReleaseBuffer(); + return ret; +} + +EXTERN_C HRESULT WINAPI +SHGetPathCchFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath, SIZE_T cchPathMax); + +static HRESULT +DynamicSHGetPathFromIDListW( + _In_ LPCITEMIDLIST pidl, + _Out_ CStringW& strPath) +{ + HRESULT hr; + + for (UINT cchPath = MAX_PATH;; cchPath *= 2) + { + LPWSTR lpszPath = strPath.GetBuffer(cchPath); + if (!lpszPath) + return E_OUTOFMEMORY; + + hr = SHGetPathCchFromIDListW(pidl, lpszPath, cchPath); + strPath.ReleaseBuffer(); + + if (hr != E_NOT_SUFFICIENT_BUFFER) + break; + + if (cchPath >= MAXUINT / 2) + { + hr = E_FAIL; + break; + } + } + + if (FAILED(hr)) + strPath.Empty(); + + return hr; +} + +static HRESULT +DynamicSHGetSpecialFolderPathW( + _In_ HWND hwndOwner, + _Out_ CStringW& strPath, + _In_ INT nCSIDL, + _In_ BOOL bCreate) +{ + LPITEMIDLIST pidl; + HRESULT hr = SHGetSpecialFolderLocation(hwndOwner, nCSIDL, &pidl); + if (SUCCEEDED(hr)) + { + hr = DynamicSHGetPathFromIDListW(pidl, strPath); + CoTaskMemFree(pidl); + } + + if (FAILED(hr)) + strPath.Empty(); + else if (bCreate) + CreateDirectoryW(strPath, NULL); + + return hr; +} + +static VOID +DynamicPathRemoveBackslashW( + _Out_ CStringW& strPath) +{ + INT nLength = strPath.GetLength(); + if (nLength > 0 && strPath[nLength - 1] == L'\\') + strPath = strPath.Left(nLength - 1); +} + +/************************************************************************* + * PathIsEqualOrSubFolder (SHELL32.755) + */ +EXTERN_C +BOOL WINAPI +PathIsEqualOrSubFolder( + _In_ LPCWSTR pszPath1OrCSIDL, + _In_ LPCWSTR pszPath2) +{ + CStringW strCommon, strPath1; + + TRACE("(%s %s)\n", debugstr_w(pszPath1OrCSIDL), debugstr_w(pszPath2)); + + if (IS_INTRESOURCE(pszPath1OrCSIDL)) + { + DynamicSHGetSpecialFolderPathW( + NULL, strPath1, LOWORD(pszPath1OrCSIDL) | CSIDL_FLAG_DONT_VERIFY, FALSE); + } + else + { + strPath1 = pszPath1OrCSIDL; + } + + DynamicPathRemoveBackslashW(strPath1); + + if (!DynamicPathCommonPrefixW(strPath1, pszPath2, strCommon)) + return FALSE; + + return strPath1.CompareNoCase(strCommon) == 0; +} diff --git a/dll/win32/shell32/wine/pidl.c b/dll/win32/shell32/wine/pidl.c index 9ade43d1956..7279737c8eb 100644 --- a/dll/win32/shell32/wine/pidl.c +++ b/dll/win32/shell32/wine/pidl.c @@ -1295,7 +1295,11 @@ BOOL WINAPI SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath) * * See SHGetPathFromIDListA. */ -BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath) +HRESULT WINAPI +SHGetPathCchFromIDListW( + _In_ LPCITEMIDLIST pidl, + _Out_writes_(cchPathMax) LPWSTR pszPath, + _In_ SIZE_T cchPathMax) { HRESULT hr; LPCITEMIDLIST pidlLast; @@ -1306,33 +1310,40 @@ BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath) TRACE_(shell)("(pidl=%p,%p)\n", pidl, pszPath); pdump(pidl); - *pszPath = '\0'; + *pszPath = UNICODE_NULL; if (!pidl) - return FALSE; + return E_FAIL; hr = SHBindToParent(pidl, &IID_IShellFolder, (VOID**)&psfFolder, &pidlLast); if (FAILED(hr)) { ERR("SHBindToParent failed: %x\n", hr); - return FALSE; + return hr; } dwAttributes = SFGAO_FILESYSTEM; hr = IShellFolder_GetAttributesOf(psfFolder, 1, &pidlLast, &dwAttributes); - if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) { + if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) + { WARN("Wrong dwAttributes or GetAttributesOf failed: %x\n", hr); IShellFolder_Release(psfFolder); - return FALSE; + return E_FAIL; } - + hr = IShellFolder_GetDisplayNameOf(psfFolder, pidlLast, SHGDN_FORPARSING, &strret); IShellFolder_Release(psfFolder); - if (FAILED(hr)) return FALSE; + if (FAILED(hr)) + return hr; - hr = StrRetToBufW(&strret, pidlLast, pszPath, MAX_PATH); + hr = StrRetToBufW(&strret, pidlLast, pszPath, cchPathMax); TRACE_(shell)("-- %s, 0x%08x\n",debugstr_w(pszPath), hr); - return SUCCEEDED(hr); + return hr; +} + +BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath) +{ + return SUCCEEDED(SHGetPathCchFromIDListW(pidl, pszPath, MAX_PATH)); } /************************************************************************* diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index 07ba4a96ca6..82c1eeea279 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND SOURCE Int64ToString.cpp IShellFolderViewCB.cpp OpenAs_RunDLL.cpp + PathIsEqualOrSubFolder.cpp PathResolve.cpp SHAppBarMessage.cpp SHChangeNotify.cpp diff --git a/modules/rostests/apitests/shell32/PathIsEqualOrSubFolder.cpp b/modules/rostests/apitests/shell32/PathIsEqualOrSubFolder.cpp new file mode 100644 index 00000000000..59701738e3e --- /dev/null +++ b/modules/rostests/apitests/shell32/PathIsEqualOrSubFolder.cpp @@ -0,0 +1,41 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for PathIsEqualOrSubFolder + * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ + */ + +#include "shelltest.h" +#include + +START_TEST(PathIsEqualOrSubFolder) +{ + ok_int(PathIsEqualOrSubFolder(L"C:", L"C:"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:", L"C:\\"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:\\", L"C:"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:\\", L"C:\\"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:\\", L"C:\\TestTestTest"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:\\TestTestTest", L"C:\\"), FALSE); + ok_int(PathIsEqualOrSubFolder(L"C:\\TestTestTest", L"C:\\TestTestTest"), TRUE); + ok_int(PathIsEqualOrSubFolder(L"C:\\TestTestTest", L"C:\\TestTestTest\\"), TRUE); + + WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; + + GetWindowsDirectoryW(szPath1, _countof(szPath1)); + ok_int(PathIsEqualOrSubFolder(szPath1, szPath1), TRUE); + + GetWindowsDirectoryW(szPath2, _countof(szPath2)); + PathAppendW(szPath2, L"TestTestTest"); + + ok_int(PathIsEqualOrSubFolder(szPath1, szPath2), TRUE); + ok_int(PathIsEqualOrSubFolder(szPath2, szPath1), FALSE); + ok_int(PathIsEqualOrSubFolder(szPath2, szPath2), TRUE); + + GetTempPathW(_countof(szPath1), szPath1); + GetTempPathW(_countof(szPath2), szPath2); + PathAppendW(szPath2, L"TestTestTest"); + + ok_int(PathIsEqualOrSubFolder(szPath1, szPath2), TRUE); + ok_int(PathIsEqualOrSubFolder(szPath2, szPath1), FALSE); + ok_int(PathIsEqualOrSubFolder(szPath2, szPath2), TRUE); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index 424e878d3b9..1a4952cfc17 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -21,6 +21,7 @@ extern void func_Int64ToString(void); extern void func_IShellFolderViewCB(void); extern void func_menu(void); extern void func_OpenAs_RunDLL(void); +extern void func_PathIsEqualOrSubFolder(void); extern void func_PathResolve(void); extern void func_SHAppBarMessage(void); extern void func_SHChangeNotify(void); @@ -57,6 +58,7 @@ const struct test winetest_testlist[] = { "IShellFolderViewCB", func_IShellFolderViewCB }, { "menu", func_menu }, { "OpenAs_RunDLL", func_OpenAs_RunDLL }, + { "PathIsEqualOrSubFolder", func_PathIsEqualOrSubFolder }, { "PathResolve", func_PathResolve }, { "SHAppBarMessage", func_SHAppBarMessage }, { "SHChangeNotify", func_SHChangeNotify }, diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h index 8fad6faa6a8..b768552f708 100644 --- a/sdk/include/reactos/undocshell.h +++ b/sdk/include/reactos/undocshell.h @@ -498,6 +498,8 @@ BOOL WINAPI PathIsSameRootAW(LPCVOID lpszPath1, LPCVOID lpszPath2); BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID *sOtherDirs); +BOOL WINAPI PathIsEqualOrSubFolder(_In_ LPCWSTR pszFile1OrCSIDL, _In_ LPCWSTR pszFile2); + /**************************************************************************** * Shell File Operations error codes - SHFileOperationA/W */