Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48082 Signed-off-by: Gijs Vermeulen gijsvrm@codeweavers.com --- dlls/shell32/shellpath.c | 69 ++++++++++++++++++++++++++++++++++------ include/shlobj.h | 3 +- 2 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/dlls/shell32/shellpath.c b/dlls/shell32/shellpath.c index 06be794812..b6350ea060 100644 --- a/dlls/shell32/shellpath.c +++ b/dlls/shell32/shellpath.c @@ -684,27 +684,78 @@ BOOL WINAPI PathQualifyAW(LPCVOID pszPath) return PathQualifyA(pszPath); }
-static BOOL PathResolveA(LPSTR path, LPCSTR *paths, DWORD flags) +BOOL WINAPI PathFindOnPathExA(LPSTR,LPCSTR *,DWORD); +BOOL WINAPI PathFindOnPathExW(LPWSTR,LPCWSTR *,DWORD); +BOOL WINAPI PathFileExistsDefExtA(LPSTR,DWORD); +BOOL WINAPI PathFileExistsDefExtW(LPWSTR,DWORD); + +static BOOL PathResolveA(char *path, const char **dirs, DWORD flags) { - FIXME("(%s,%p,0x%08x),stub!\n", debugstr_a(path), paths, flags); - return FALSE; + BOOL is_file_spec = PathIsFileSpecA(path); + DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff; + + TRACE("(%s,%p,0x%08x)\n", debugstr_a(path), dirs, flags); + + if (flags & PRF_VERIFYEXISTS && !PathFileExistsA(path)) + { + if (PathFindOnPathExA(path, dirs, dwWhich)) + return TRUE; + if (PathFileExistsDefExtA(path, dwWhich)) + return TRUE; + if (!is_file_spec) GetFullPathNameA(path, MAX_PATH, path, NULL); + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if (is_file_spec) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + GetFullPathNameA(path, MAX_PATH, path, NULL); + + return TRUE; }
-static BOOL PathResolveW(LPWSTR path, LPCWSTR *paths, DWORD flags) +static BOOL PathResolveW(WCHAR *path, const WCHAR **dirs, DWORD flags) { - FIXME("(%s,%p,0x%08x),stub!\n", debugstr_w(path), paths, flags); - return FALSE; + BOOL is_file_spec = PathIsFileSpecW(path); + DWORD dwWhich = flags & PRF_DONTFINDLNK ? 0xf : 0xff; + + TRACE("(%s,%p,0x%08x)\n", debugstr_w(path), dirs, flags); + + if (flags & PRF_VERIFYEXISTS && !PathFileExistsW(path)) + { + if (PathFindOnPathExW(path, dirs, dwWhich)) + return TRUE; + if (PathFileExistsDefExtW(path, dwWhich)) + return TRUE; + if (!is_file_spec) GetFullPathNameW(path, MAX_PATH, path, NULL); + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + if (is_file_spec) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } + + GetFullPathNameW(path, MAX_PATH, path, NULL); + + return TRUE; }
/************************************************************************* * PathResolve [SHELL32.51] */ -BOOL WINAPI PathResolveAW(LPVOID path, LPCVOID *paths, DWORD flags) +BOOL WINAPI PathResolveAW(void *path, const void **paths, DWORD flags) { if (SHELL_OsIsUnicode()) - return PathResolveW(path, (LPCWSTR*)paths, flags); + return PathResolveW(path, (const WCHAR **)paths, flags); else - return PathResolveA(path, (LPCSTR*)paths, flags); + return PathResolveA(path, (const char **)paths, flags); }
/************************************************************************* diff --git a/include/shlobj.h b/include/shlobj.h index 9a7741a193..bc1150bf91 100644 --- a/include/shlobj.h +++ b/include/shlobj.h @@ -1752,10 +1752,9 @@ BOOL WINAPI WriteCabinetState(CABINETSTATE *);
/* PathResolve flags */ #define PRF_VERIFYEXISTS 0x01 -#define PRF_EXECUTABLE 0x02 #define PRF_TRYPROGRAMEXTENSIONS 0x03 #define PRF_FIRSTDIRDEF 0x04 -#define PRF_DONTFINDLINK 0x08 +#define PRF_DONTFINDLNK 0x08 #define PRF_REQUIREABSOLUTE 0x10
VOID WINAPI PathGetShortPath(LPWSTR pszPath);
Signed-off-by: Gijs Vermeulen gijsvrm@codeweavers.com --- dlls/shell32/tests/shellpath.c | 119 +++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+)
diff --git a/dlls/shell32/tests/shellpath.c b/dlls/shell32/tests/shellpath.c index 262714a9f3..4ea2ef14cd 100644 --- a/dlls/shell32/tests/shellpath.c +++ b/dlls/shell32/tests/shellpath.c @@ -99,6 +99,7 @@ static HRESULT (WINAPI *pSHSetKnownFolderPath)(REFKNOWNFOLDERID, DWORD, HANDLE, static HRESULT (WINAPI *pSHGetFolderPathEx)(REFKNOWNFOLDERID, DWORD, HANDLE, LPWSTR, DWORD); static BOOL (WINAPI *pPathYetAnotherMakeUniqueName)(PWSTR, PCWSTR, PCWSTR, PCWSTR); static HRESULT (WINAPI *pSHGetKnownFolderIDList)(REFKNOWNFOLDERID, DWORD, HANDLE, PIDLIST_ABSOLUTE*); +static BOOL (WINAPI *pPathResolve)(PWSTR, PZPCWSTR, UINT);
static DLLVERSIONINFO shellVersion = { 0 }; static LPMALLOC pMalloc; @@ -207,6 +208,7 @@ static void loadShell32(void) GET_PROC(SHGetMalloc) GET_PROC(PathYetAnotherMakeUniqueName) GET_PROC(SHGetKnownFolderIDList) + GET_PROC(PathResolve);
ok(pSHGetMalloc != NULL, "shell32 is missing SHGetMalloc\n"); if (pSHGetMalloc) @@ -2864,6 +2866,122 @@ if (0) { /* crashes on native */ ILFree(pidl); }
+static void test_PathResolve(void) +{ + WCHAR testfile[MAX_PATH], testfile_lnk[MAX_PATH], regedit_in_testdir[MAX_PATH], regedit_cmd[MAX_PATH]; + WCHAR tempdir[MAX_PATH], path[MAX_PATH]; + const WCHAR *dirs[2] = { tempdir, NULL }; + HANDLE file, file2; + BOOL ret; + int i; + struct { + const WCHAR *path; + UINT flags; + BOOL expected; + const WCHAR *expected_path; + } tests[] = { + /* no flags */ + { L"test", 0, FALSE, L"test" }, + { L"C:\test", 0, TRUE, L"C:\test" }, + { L"regedit", 0, FALSE, L"regedit" }, + { testfile, 0, TRUE, testfile }, + + /* PRF_VERIFYEXISTS */ + { L"test", PRF_VERIFYEXISTS, TRUE, testfile_lnk }, + { L"C:\test", PRF_VERIFYEXISTS, FALSE, L"C:\test" }, + /* common extensions are tried even if PRF_TRYPROGRAMEXTENSIONS isn't passed */ + /* directories in dirs parameter are always searched first even if PRF_FIRSTDIRDEF isn't passed */ + { L"regedit", PRF_VERIFYEXISTS, TRUE, regedit_cmd }, + /* .dll is not tried */ + { L"bcrypt", PRF_VERIFYEXISTS, FALSE, L"bcrypt" }, + { testfile, PRF_VERIFYEXISTS, TRUE, testfile_lnk }, + { regedit_in_testdir, PRF_VERIFYEXISTS, TRUE, regedit_cmd }, + + /* PRF_FIRSTDIRDEF */ + { L"regedit", PRF_FIRSTDIRDEF, FALSE, L"regedit" }, + + /* RF_VERIFYEXISTS | PRF_FIRSTDIRDEF */ + { L"regedit", PRF_VERIFYEXISTS | PRF_FIRSTDIRDEF, TRUE, regedit_cmd }, + + /* PRF_DONTFINDLNK */ + { testfile, PRF_DONTFINDLNK, TRUE, testfile }, + { regedit_in_testdir, PRF_DONTFINDLNK, TRUE, regedit_in_testdir }, + + /* RF_VERIFYEXISTS | PRF_DONTFINDLNK */ + { testfile, PRF_VERIFYEXISTS | PRF_DONTFINDLNK, FALSE, testfile }, + /* cmd is also ignored when passing PRF_VERIFYEXISTS | PRF_DONTFINDLNK */ + { regedit_in_testdir, PRF_VERIFYEXISTS | PRF_DONTFINDLNK, FALSE, regedit_in_testdir }, + + /* PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE */ + /* only PRF_VERIFYEXISTS matters*/ + { L"test", PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE, TRUE, testfile_lnk }, + { L"C:\test", PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE, FALSE, L"C:\test" }, + { L"regedit", PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE, TRUE, regedit_cmd }, + { testfile, PRF_VERIFYEXISTS | PRF_REQUIREABSOLUTE, TRUE, testfile_lnk }, + + /* PRF_TRYPROGRAMEXTENSIONS */ + { L"test", PRF_TRYPROGRAMEXTENSIONS, TRUE, testfile_lnk}, + { L"C:\test", PRF_TRYPROGRAMEXTENSIONS, FALSE, L"C:\test" }, + { L"regedit", PRF_TRYPROGRAMEXTENSIONS, TRUE, regedit_cmd }, + /* .dll is not tried */ + { L"bcrypt", PRF_TRYPROGRAMEXTENSIONS, FALSE, L"bcrypt" }, + { testfile, PRF_TRYPROGRAMEXTENSIONS, TRUE, testfile_lnk }, + { regedit_in_testdir, PRF_TRYPROGRAMEXTENSIONS, TRUE, regedit_cmd }, + + /* PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK */ + { testfile, PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK, FALSE, testfile }, + /* cmd is also ignored when passing PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK */ + { regedit_in_testdir, PRF_TRYPROGRAMEXTENSIONS | PRF_DONTFINDLNK, FALSE, regedit_in_testdir } + }; + + if (!pPathResolve) + { + win_skip("PathResolve not available\n"); + return; + } + + GetTempPathW(MAX_PATH, tempdir); + + lstrcpyW(testfile, tempdir); + lstrcatW(testfile, L"test"); + lstrcpyW(testfile_lnk, testfile); + lstrcatW(testfile_lnk, L".lnk"); + + file = CreateFileW(testfile_lnk, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); + ok(file != INVALID_HANDLE_VALUE, "got %p\n", file); + + lstrcpyW(regedit_in_testdir, tempdir); + lstrcatW(regedit_in_testdir, L"regedit"); + lstrcpyW(regedit_cmd, regedit_in_testdir); + lstrcatW(regedit_cmd, L".cmd"); + + file2 = CreateFileW(regedit_cmd, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, NULL); + ok(file2 != INVALID_HANDLE_VALUE, "got %p\n", file); + + /* show that resolving regedit with NULL dirs returns regedit.exe */ + lstrcpyW(path, L"regedit"); + ret = pPathResolve(path, NULL, PRF_VERIFYEXISTS); + ok(ret, "resolving regedit failed unexpectedly\n"); + ok(!lstrcmpiW(path, L"C:\windows\regedit.exe") || !lstrcmpiW(path, L"C:\windows\system32\regedit.exe"), + "unexpected path %s\n", wine_dbgstr_w(path)); + + for (i = 0; i < ARRAY_SIZE(tests); i++) + { + lstrcpyW(path, tests[i].path); + + if (!tests[i].expected) SetLastError(0xdeadbeef); + ret = pPathResolve(path, dirs, tests[i].flags); + ok(ret == tests[i].expected, "test %d: expected %d, got %d\n", i, tests[i].expected, ret); + ok(!lstrcmpiW(path, tests[i].expected_path), + "test %d: expected %s, got %s\n", i, wine_dbgstr_w(tests[i].expected_path), wine_dbgstr_w(path)); + if (!tests[i].expected) + ok(GetLastError() == ERROR_FILE_NOT_FOUND, "expected ERROR_ALREADY_EXISTS, got %d\n", GetLastError()); + } + + CloseHandle(file); + CloseHandle(file2); +} + START_TEST(shellpath) { if (!init()) return; @@ -2894,5 +3012,6 @@ START_TEST(shellpath) test_DoEnvironmentSubst(); test_PathYetAnotherMakeUniqueName(); test_SHGetKnownFolderIDList(); + test_PathResolve(); } }