https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3a822e4f74ea6ea8335a8…
commit 3a822e4f74ea6ea8335a8684c5071d264fee4bed
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Mon Jun 28 07:53:26 2021 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Mon Jun 28 07:53:26 2021 +0900
[SHELL32] Implement PathResolveW function (#3762)
- Implement PathResolveW function.
- Implement PathQualifyA/W functions using newly-defined PathQualifyExW function.
CORE-12665
---
dll/win32/shell32/CShellLink.cpp | 125 +++----------------
dll/win32/shell32/wine/shellpath.c | 232 +++++++++++++++++++++++++++++++++---
sdk/include/reactos/shlwapi_undoc.h | 12 ++
sdk/include/reactos/undocshell.h | 7 +-
4 files changed, 249 insertions(+), 127 deletions(-)
diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp
index 7da9b8cf916..cd5d74e2c3f 100644
--- a/dll/win32/shell32/CShellLink.cpp
+++ b/dll/win32/shell32/CShellLink.cpp
@@ -2178,108 +2178,6 @@ HRESULT CShellLink::SetAdvertiseInfo(LPCWSTR str)
return S_OK;
}
-/*
- * Since the real PathResolve (from Wine) is unimplemented at the moment,
- * we use this local implementation, until a better one is written (using
- * code parts of the SHELL_xxx helpers in Wine's shellpath.c).
- */
-static BOOL HACKISH_PathResolve(
- IN OUT PWSTR pszPath,
- IN PZPCWSTR dirs OPTIONAL,
- IN UINT fFlags)
-{
- // FIXME: This is unimplemented!!!
-#if 0
- return PathResolve(pszPath, dirs, fFlags);
-#else
- BOOL Success = FALSE;
- USHORT i;
- LPWSTR fname = NULL;
- WCHAR szPath[MAX_PATH];
-
- /* First, search for a valid existing path */
-
- // NOTE: See also: SHELL_FindExecutable()
-
- /*
- * List of extensions searched for, by PathResolve with the flag
- * PRF_TRYPROGRAMEXTENSIONS == PRF_EXECUTABLE | PRF_VERIFYEXISTS set,
- * according to MSDN:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb776478(v=vs.85).…
- */
- static PCWSTR Extensions[] = {L".pif", L".com",
L".bat", L".cmd", L".lnk", L".exe", NULL};
- #define LNK_EXT_INDEX 4 // ".lnk" has index 4 in the array above
-
- /*
- * Start at the beginning of the list if PRF_EXECUTABLE is set, otherwise
- * just use the last element 'NULL' (no extension checking).
- */
- i = ((fFlags & PRF_EXECUTABLE) ? 0 : _countof(Extensions) - 1);
- for (; i < _countof(Extensions); ++i)
- {
- /* Ignore shell links ".lnk" if needed */
- if ((fFlags & PRF_DONTFINDLNK) && (i == LNK_EXT_INDEX))
- continue;
-
- Success = (SearchPathW(NULL, pszPath, Extensions[i],
- _countof(szPath), szPath, NULL) != 0);
- if (!Success)
- {
- ERR("SearchPathW(pszPath = '%S') failed. Error code:
%lu\n", pszPath, GetLastError());
- }
- else
- {
- ERR("SearchPathW(pszPath = '%S', szPath = '%S')
succeeded\n", pszPath, szPath);
- break;
- }
- }
-
- if (!Success)
- {
- ERR("SearchPathW(pszPath = '%S') failed. Error code: %lu\n",
pszPath, GetLastError());
-
- /* We failed, try with PathFindOnPath, as explained by MSDN */
- // Success = PathFindOnPathW(pszPath, dirs);
- StringCchCopyW(szPath, _countof(szPath), pszPath);
- Success = PathFindOnPathW(szPath, dirs);
- if (!Success)
- {
- ERR("PathFindOnPathW(pszPath = '%S') failed\n", pszPath);
-
- /* We failed again, fall back to building a possible non-existing path */
- if (!GetFullPathNameW(pszPath, _countof(szPath), szPath, &fname))
- {
- ERR("GetFullPathNameW(pszPath = '%S') failed. Error code:
%lu\n", pszPath, GetLastError());
- return FALSE;
- }
-
- Success = PathFileExistsW(szPath);
- if (!Success)
- ERR("PathFileExistsW(szPath = '%S') failed. Error code:
%lu\n", szPath, GetLastError());
-
- /******************************************************/
- /* Question: Why this line is needed only for files?? */
- if (fname && (_wcsicmp(pszPath, fname) == 0))
- *szPath = L'\0';
- /******************************************************/
- }
- else
- {
- ERR("PathFindOnPathW(pszPath = '%S' ==> '%S')
succeeded\n", pszPath, szPath);
- }
- }
-
- /* Copy back the results to the caller */
- StringCchCopyW(pszPath, MAX_PATH, szPath);
-
- /*
- * Since the called functions always checked whether the file path existed,
- * we do not need to redo a final check: we can use instead the cached
- * result in 'Success'.
- */
- return ((fFlags & PRF_VERIFYEXISTS) ? Success : TRUE);
-#endif
-}
-
HRESULT CShellLink::SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl, LPCWSTR pszFile)
{
HRESULT hr = S_OK;
@@ -2309,14 +2207,21 @@ HRESULT CShellLink::SetTargetFromPIDLOrPath(LPCITEMIDLIST pidl,
LPCWSTR pszFile)
/* This failed, try to resolve the path, then create a simple PIDL */
StringCchCopyW(szPath, _countof(szPath), pszFile);
- // FIXME: Because PathResolve is unimplemented, we use our hackish
implementation!
- HACKISH_PathResolve(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS);
-
- pidlNew = SHSimpleIDListFromPathW(szPath);
- /******************************************************/
- /* Question: Why this line is needed only for files?? */
- hr = (*szPath ? S_OK : E_INVALIDARG); // S_FALSE
- /******************************************************/
+ PathResolveW(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS);
+
+ if (PathIsFileSpecW(szPath))
+ {
+ hr = E_INVALIDARG;
+ szPath[0] = 0;
+ }
+ else
+ {
+ hr = S_OK;
+ pidlNew = SHSimpleIDListFromPathW(szPath);
+ // NOTE: Don't make it failed here even if pidlNew was NULL.
+ // We don't fail on purpose even if SHSimpleIDListFromPathW returns
NULL.
+ // This behaviour has been verified with tests.
+ }
}
}
// else if (!pidl && !pszFile) { pidlNew = NULL; hr = S_OK; }
diff --git a/dll/win32/shell32/wine/shellpath.c b/dll/win32/shell32/wine/shellpath.c
index 892748d56b7..e4234efffcd 100644
--- a/dll/win32/shell32/wine/shellpath.c
+++ b/dll/win32/shell32/wine/shellpath.c
@@ -3,7 +3,7 @@
*
* Copyright 1998, 1999, 2000 Juergen Schmied
* Copyright 2004 Juan Lang
- * Copyright 2018-2020 Katayama Hirofumi MZ
+ * Copyright 2018-2021 Katayama Hirofumi MZ
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -50,6 +50,9 @@
#include "shell32_main.h"
#include "shresdef.h"
+#undef _WIN32_WINNT
+#define _WIN32_WINNT _WIN32_WINNT_WS03
+
WINE_DEFAULT_DEBUG_CHANNEL(shell);
static const BOOL is_win64 = sizeof(void *) > sizeof(int);
@@ -106,6 +109,127 @@ DoGetProductType(PNT_PRODUCT_TYPE ProductType)
########## Combining and Constructing paths ##########
*/
+/* @implemented */
+static BOOL WINAPI
+PathSearchOnExtensionsW(LPWSTR pszPath, LPCWSTR *ppszDirs, BOOL bDoSearch, DWORD
dwWhich)
+{
+ if (*PathFindExtensionW(pszPath) != 0)
+ return FALSE;
+
+ if (bDoSearch)
+ return PathFindOnPathExW(pszPath, ppszDirs, dwWhich);
+ else
+ return PathFileExistsDefExtW(pszPath, dwWhich);
+}
+
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+/* @implemented */
+static BOOL WINAPI PathIsAbsoluteW(LPCWSTR path)
+{
+ return PathIsUNCW(path) || (PathGetDriveNumberW(path) != -1 && path[2] ==
L'\\');
+}
+
+/* @implemented */
+static BOOL WINAPI PathMakeAbsoluteW(LPWSTR path)
+{
+ WCHAR path1[MAX_PATH];
+ DWORD cch;
+
+ if (path == NULL)
+ return FALSE;
+ cch = GetCurrentDirectoryW(_countof(path1), path1);
+ if (!cch || cch > _countof(path1))
+ return FALSE;
+ return (PathCombineW(path, path1, path) != NULL);
+}
+#endif
+
+/* NOTE: GetShortPathName fails if the pathname didn't exist.
+ GetShortPathNameAbsentW should set the short path name that even doesn't
exist. */
+static DWORD GetShortPathNameAbsentW(LPCWSTR pszLong, LPWSTR pszShort, DWORD cchShort)
+{
+ FIXME("GetShortPathNameAbsentW(%ls, %p, %ld): stub\n", pszLong, pszShort,
cchShort);
+ StringCchCopyW(pszShort, cchShort, pszLong);
+ return lstrlenW(pszShort);
+}
+
+BOOL WINAPI IsLFNDriveW(LPCWSTR lpszPath);
+
+/* @unconfirmed */
+static VOID WINAPI PathQualifyExW(LPWSTR pszPath, LPCWSTR pszDir, DWORD dwFlags)
+{
+ WCHAR szRoot[MAX_PATH], szCopy[MAX_PATH], szCurDir[MAX_PATH];
+ LPWSTR pch;
+ LONG cch;
+ BOOL bCheckLFN;
+
+ if (FAILED(StringCchCopyW(szCopy, _countof(szCopy), pszPath)))
+ return;
+
+ FixSlashesAndColonW(szCopy);
+
+ if (pszDir)
+ {
+ cch = GetCurrentDirectoryW(_countof(szCurDir), szCurDir);
+ if (cch <= 0 || cch >= _countof(szCurDir) ||
!SetCurrentDirectoryW(pszDir))
+ pszDir = NULL;
+ }
+
+ if (!GetFullPathNameW(szCopy, _countof(szRoot), szRoot, NULL))
+ goto Quit;
+
+ if (PathIsUNCW(szRoot)) /* it begins with double backslash */
+ {
+ pch = StrChrW(&szRoot[2], L'\\');
+ if (pch)
+ {
+ pch = StrChrW(&pch[1], L'\\');
+ if (pch)
+ *pch = 0;
+ if (!PathAddBackslashW(szRoot))
+ goto Quit;
+ /* szRoot is like \\MyServer\MyShare\ */
+ bCheckLFN = TRUE;
+ }
+ else
+ {
+ bCheckLFN = FALSE;
+ }
+ }
+ else
+ {
+ if (!PathStripToRootW(szRoot) || !PathAddBackslashW(szRoot))
+ goto Quit;
+ /* szRoot is like X:\ */
+ bCheckLFN = TRUE;
+ }
+
+ if (bCheckLFN && !IsLFNDriveW(szRoot)) /* not a long filename drive */
+ {
+ if (!GetFullPathNameW(szCopy, _countof(szRoot), szRoot, NULL))
+ goto Quit;
+ if (!GetShortPathNameW(szRoot, szCopy, _countof(szCopy)) &&
+ !GetShortPathNameAbsentW(szRoot, szCopy, _countof(szCopy)))
+ {
+ goto Quit;
+ }
+ }
+
+ PathRemoveBackslashW(szCopy);
+ StringCchCopyW(pszPath, MAX_PATH, szCopy);
+
+ if ((dwFlags & 1) == 0)
+ {
+ cch = lstrlenW(pszPath);
+ if (cch > 0 && pszPath[cch - 1] == L'.')
+ pszPath[cch - 1] = 0;
+ }
+
+Quit:
+ if (pszDir)
+ SetCurrentDirectoryW(szCurDir);
+}
+
/*************************************************************************
* PathAppend [SHELL32.36]
*/
@@ -476,40 +600,118 @@ int WINAPI PathCleanupSpec( LPCWSTR lpszPathW, LPWSTR lpszFileW )
/*************************************************************************
* PathQualifyA [SHELL32]
*/
-static BOOL PathQualifyA(LPCSTR pszPath)
+VOID WINAPI PathQualifyA(LPSTR pszPath)
{
- FIXME("%s\n",pszPath);
- return FALSE;
+ WCHAR szPath[MAX_PATH];
+ TRACE("%s\n",pszPath);
+ SHAnsiToUnicode(pszPath, szPath, _countof(szPath));
+ PathQualifyW(szPath);
+ SHUnicodeToAnsi(szPath, pszPath, MAX_PATH);
}
/*************************************************************************
* PathQualifyW [SHELL32]
*/
-static BOOL PathQualifyW(LPCWSTR pszPath)
+VOID WINAPI PathQualifyW(LPWSTR pszPath)
{
- FIXME("%s\n",debugstr_w(pszPath));
- return FALSE;
+ TRACE("%s\n",debugstr_w(pszPath));
+ PathQualifyExW(pszPath, NULL, 0);
}
/*************************************************************************
* PathQualify [SHELL32.49]
*/
-BOOL WINAPI PathQualifyAW(LPCVOID pszPath)
+VOID WINAPI PathQualifyAW(LPVOID pszPath)
{
- if (SHELL_OsIsUnicode())
- return PathQualifyW(pszPath);
- return PathQualifyA(pszPath);
+ if (SHELL_OsIsUnicode())
+ PathQualifyW(pszPath);
+ else
+ PathQualifyA(pszPath);
}
-static BOOL PathResolveA(LPSTR path, LPCSTR *paths, DWORD flags)
+BOOL WINAPI PathResolveA(LPSTR path, LPCSTR *dirs, DWORD flags)
{
- FIXME("(%s,%p,0x%08x),stub!\n", debugstr_a(path), paths, flags);
+ FIXME("(%s,%p,0x%08x),stub!\n", debugstr_a(path), dirs, flags);
return FALSE;
}
-static BOOL PathResolveW(LPWSTR path, LPCWSTR *paths, DWORD flags)
+#define WHICH_DONTFINDLNK (WHICH_PIF | WHICH_COM | WHICH_EXE | WHICH_BAT)
+#define WHICH_DEFAULT (WHICH_DONTFINDLNK | WHICH_LNK | WHICH_CMD)
+
+BOOL WINAPI PathResolveW(LPWSTR path, LPCWSTR *dirs, DWORD flags)
{
- FIXME("(%s,%p,0x%08x),stub!\n", debugstr_w(path), paths, flags);
+ DWORD dwWhich;
+ TRACE("PathResolveW(%s,%p,0x%08x)\n", debugstr_w(path), dirs, flags);
+ dwWhich = ((flags & PRF_DONTFINDLNK) ? WHICH_DONTFINDLNK : WHICH_DEFAULT);
+
+ if (flags & PRF_VERIFYEXISTS)
+ SetLastError(ERROR_FILE_NOT_FOUND);
+
+ PathUnquoteSpacesW(path);
+
+ if (PathIsRootW(path))
+ {
+ if ((path[0] == L'\\' && path[1] == 0) ||
+ PathIsUNCServerW(path) || PathIsUNCServerShareW(path))
+ {
+ if (flags & PRF_FIRSTDIRDEF)
+ PathQualifyExW(path, dirs[0], 0);
+ else
+ PathQualifyExW(path, NULL, 0);
+ }
+
+ if (flags & PRF_VERIFYEXISTS)
+ return PathFileExistsAndAttributesW(path, NULL);
+ return TRUE;
+ }
+ else if (PathIsFileSpecW(path))
+ {
+ if ((flags & PRF_TRYPROGRAMEXTENSIONS) &&
PathSearchOnExtensionsW(path, dirs, TRUE, dwWhich))
+ return TRUE;
+
+ if (PathFindOnPathW(path, dirs))
+ {
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+ if (!(flags & PRF_REQUIREABSOLUTE))
+ return TRUE;
+
+ if (!PathIsAbsoluteW(path))
+ return PathMakeAbsoluteW(path) &&
PathFileExistsAndAttributesW(path, NULL);
+#else
+ return TRUE;
+#endif
+ }
+ }
+ else if (!PathIsURLW(path))
+ {
+ if (flags & PRF_FIRSTDIRDEF)
+ PathQualifyExW(path, *dirs, 1);
+ else
+ PathQualifyExW(path, NULL, 1);
+
+ if (flags & PRF_VERIFYEXISTS)
+ {
+ if ((flags & PRF_TRYPROGRAMEXTENSIONS) &&
+ PathSearchOnExtensionsW(path, dirs, FALSE, dwWhich))
+ {
+ return TRUE;
+ }
+ else if (!PathFileExistsAndAttributesW(path, NULL))
+ {
+ return FALSE;
+ }
+ }
+
+#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
+ if (flags & PRF_REQUIREABSOLUTE)
+ {
+ if (!PathIsAbsoluteW(path))
+ return PathMakeAbsoluteW(path) &&
PathFileExistsAndAttributesW(path, NULL);
+ }
+#endif
+ return TRUE;
+ }
+
return FALSE;
}
diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h
index c1f92559e55..ed54a70c686 100644
--- a/sdk/include/reactos/shlwapi_undoc.h
+++ b/sdk/include/reactos/shlwapi_undoc.h
@@ -150,6 +150,18 @@ ShellMessageBoxWrapW(
_In_ UINT fuStyle,
...);
+/* dwWhich flags for PathFileExistsDefExtW and PathFindOnPathExW */
+#define WHICH_PIF (1 << 0)
+#define WHICH_COM (1 << 1)
+#define WHICH_EXE (1 << 2)
+#define WHICH_BAT (1 << 3)
+#define WHICH_LNK (1 << 4)
+#define WHICH_CMD (1 << 5)
+
+BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath, DWORD dwWhich);
+BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs, DWORD dwWhich);
+VOID WINAPI FixSlashesAndColonW(LPWSTR);
+
#ifdef __cplusplus
} /* extern "C" */
#endif /* defined(__cplusplus) */
diff --git a/sdk/include/reactos/undocshell.h b/sdk/include/reactos/undocshell.h
index a173174cb57..0aad1cf1851 100644
--- a/sdk/include/reactos/undocshell.h
+++ b/sdk/include/reactos/undocshell.h
@@ -440,8 +440,9 @@ BOOL WINAPI PathYetAnotherMakeUniqueName(
LPCWSTR lpszShortName,
LPCWSTR lpszLongName);
-BOOL WINAPI PathQualifyAW(LPCVOID path);
-
+VOID WINAPI PathQualifyA(LPSTR pszPath);
+VOID WINAPI PathQualifyW(LPWSTR pszPath);
+VOID WINAPI PathQualifyAW(LPVOID path);
/* PathResolve flags */
#define PRF_CHECKEXISTANCE 0x01
@@ -449,6 +450,8 @@ BOOL WINAPI PathQualifyAW(LPCVOID path);
#define PRF_QUALIFYONPATH 0x04
#define PRF_WINDOWS31 0x08
+BOOL WINAPI PathResolveA(LPSTR path, LPCSTR *dirs, DWORD flags);
+BOOL WINAPI PathResolveW(LPWSTR path, LPCWSTR *dirs, DWORD flags);
BOOL WINAPI PathResolveAW(LPVOID lpszPath, LPCVOID *alpszPaths, DWORD dwFlags);
VOID WINAPI PathSetDlgItemPathAW(HWND hDlg, int nIDDlgItem, LPCVOID lpszPath);