[SHELL32] Implement PathResolveW function (#3762)

- Implement PathResolveW function.
- Implement PathQualifyA/W functions using newly-defined PathQualifyExW function.
CORE-12665
This commit is contained in:
Katayama Hirofumi MZ 2021-06-28 07:53:26 +09:00 committed by GitHub
parent 81f8bcea8c
commit 3a822e4f74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 248 additions and 126 deletions

View file

@ -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).aspx
*/
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);
PathResolveW(szPath, NULL, PRF_TRYPROGRAMEXTENSIONS);
pidlNew = SHSimpleIDListFromPathW(szPath);
/******************************************************/
/* Question: Why this line is needed only for files?? */
hr = (*szPath ? S_OK : E_INVALIDARG); // S_FALSE
/******************************************************/
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; }

View file

@ -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;
}

View file

@ -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) */

View file

@ -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);