https://git.reactos.org/?p=reactos.git;a=commitdiff;h=2aeda3dc159a1b175a488…
commit 2aeda3dc159a1b175a48876c0aee8a8a5b2c9574
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Wed Nov 29 22:50:01 2023 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Wed Nov 29 22:50:01 2023 +0900
[SHELL32][SHELL32_APITEST][SDK] Implement PathIsEqualOrSubFolder (#5714)
Implement PathIsEqualOrSubFolder function.
- Add it to <undocshell.h>.
- Add PathIsEqualOrSubFolder testcase.
- Add SHGetPathCchFromIDListW as an
extension of SHGetPathFromIDListW.
CORE-19278
---
dll/win32/shell32/stubs.cpp | 8 --
dll/win32/shell32/utils.cpp | 116 +++++++++++++++++++++
dll/win32/shell32/wine/pidl.c | 31 ++++--
modules/rostests/apitests/shell32/CMakeLists.txt | 1 +
.../apitests/shell32/PathIsEqualOrSubFolder.cpp | 41 ++++++++
modules/rostests/apitests/shell32/testlist.c | 2 +
sdk/include/reactos/undocshell.h | 2 +
7 files changed, 183 insertions(+), 18 deletions(-)
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
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+#include "shelltest.h"
+#include <undocshell.h>
+
+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
*/