Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- .../api-ms-win-core-path-l1-1-0.spec | 2 +- dlls/kernelbase/kernelbase.spec | 2 +- dlls/kernelbase/path.c | 25 ++++ dlls/kernelbase/tests/path.c | 123 ++++++++++++++++++ include/pathcch.h | 1 + 5 files changed, 151 insertions(+), 2 deletions(-)
diff --git a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec index a722f24443..796b57f457 100644 --- a/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec +++ b/dlls/api-ms-win-core-path-l1-1-0/api-ms-win-core-path-l1-1-0.spec @@ -4,7 +4,7 @@ @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) kernelbase.PathCchAddBackslashEx @ stdcall PathCchAddExtension(wstr long wstr) kernelbase.PathCchAddExtension @ stub PathCchAppend -@ stub PathCchAppendEx +@ stdcall PathCchAppendEx(wstr long wstr long) kernelbase.PathCchAppendEx @ stdcall PathCchCanonicalize(ptr long wstr) kernelbase.PathCchCanonicalize @ stdcall PathCchCanonicalizeEx(ptr long wstr long) kernelbase.PathCchCanonicalizeEx @ stdcall PathCchCombine(ptr long wstr wstr) kernelbase.PathCchCombine diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index bad32e8db4..04023e31b6 100644 --- a/dlls/kernelbase/kernelbase.spec +++ b/dlls/kernelbase/kernelbase.spec @@ -1033,7 +1033,7 @@ @ stdcall PathCchAddBackslashEx(wstr long ptr ptr) @ stdcall PathCchAddExtension(wstr long wstr) # @ stub PathCchAppend -# @ stub PathCchAppendEx +@ stdcall PathCchAppendEx(wstr long wstr long) @ stdcall PathCchCanonicalize(ptr long wstr) @ stdcall PathCchCanonicalizeEx(ptr long wstr long) @ stdcall PathCchCombine(ptr long wstr wstr) diff --git a/dlls/kernelbase/path.c b/dlls/kernelbase/path.c index a9186a1156..c5817e26fd 100644 --- a/dlls/kernelbase/path.c +++ b/dlls/kernelbase/path.c @@ -446,6 +446,31 @@ HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extens return S_OK; }
+HRESULT WINAPI PathCchAppendEx(WCHAR *path1, SIZE_T size, const WCHAR *path2, DWORD flags) +{ + HRESULT hr; + WCHAR *result; + + TRACE("%s %lu %s %#x\n", wine_dbgstr_w(path1), size, wine_dbgstr_w(path2), flags); + + if (!path1 || !size) return E_INVALIDARG; + + /* Create a temporary buffer for result because we need to keep path1 unchanged if error occurs. + * And PathCchCombineEx writes empty result if there is error so we can't just use path1 as output + * buffer for PathCchCombineEx */ + result = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + if (!result) return E_OUTOFMEMORY; + + /* Avoid the single backslash behavior with PathCchCombineEx when appending */ + if (path2 && path2[0] == '\' && path2[1] != '\') path2++; + + hr = PathCchCombineEx(result, size, path1, path2, flags); + if (SUCCEEDED(hr)) memcpy(path1, result, size * sizeof(WCHAR)); + + HeapFree(GetProcessHeap(), 0, result); + return hr; +} + HRESULT WINAPI PathCchCanonicalize(WCHAR *out, SIZE_T size, const WCHAR *in) { TRACE("%p %lu %s\n", out, size, wine_dbgstr_w(in)); diff --git a/dlls/kernelbase/tests/path.c b/dlls/kernelbase/tests/path.c index 30528b7768..0b9b8683da 100644 --- a/dlls/kernelbase/tests/path.c +++ b/dlls/kernelbase/tests/path.c @@ -35,6 +35,7 @@ HRESULT (WINAPI *pPathAllocCombine)(const WCHAR *path1, const WCHAR *path2, DWOR HRESULT (WINAPI *pPathCchAddBackslash)(WCHAR *out, SIZE_T size); HRESULT (WINAPI *pPathCchAddBackslashEx)(WCHAR *out, SIZE_T size, WCHAR **endptr, SIZE_T *remaining); HRESULT (WINAPI *pPathCchAddExtension)(WCHAR *path, SIZE_T size, const WCHAR *extension); +HRESULT (WINAPI *pPathCchAppendEx)(WCHAR *path1, SIZE_T size, const WCHAR *path2, DWORD flags); HRESULT (WINAPI *pPathCchCanonicalize)(WCHAR *out, SIZE_T size, const WCHAR *in); HRESULT (WINAPI *pPathCchCanonicalizeEx)(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags); HRESULT (WINAPI *pPathCchCombine)(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2); @@ -786,6 +787,126 @@ static const struct addextension_test addextension_tests[] = {"C:\1.exe", " ", NULL, E_INVALIDARG} };
+struct append_test +{ + const CHAR *path1; + const CHAR *path2; + const CHAR *result; +}; + +static const struct append_test append_tests[] = +{ + /* normal paths */ + {"C:\", "a", "C:\a"}, + {"C:\b", "..\a", "C:\a"}, + {"C:", "a", "C:\a"}, + {"C:\", ".", "C:\"}, + {"C:\", "..", "C:\"}, + {"C:\a", "", "C:\a"}, + + /* normal UNC paths */ + {"\\192.168.1.1\test", "a", "\\192.168.1.1\test\a"}, + {"\\192.168.1.1\test", "..", "\\192.168.1.1"}, + {"\a", "b", "\a\b"}, + {"\", "a", "\a"}, + {"\\", "a", "\\a"}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", "a", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a"}, + + /* NT paths */ + {"\\?\C:\", "a", "C:\a"}, + {"\\?\C:\", "..", "C:\"}, + {"\\?\C:", "a", "C:\a"}, + + /* NT UNC path */ + {"\\?\UNC\", "a", "\\a"}, + {"\\?\UNC\192.168.1.1\test", "a", "\\192.168.1.1\test\a"}, + {"\\?\UNC\192.168.1.1\test", "..", "\\192.168.1.1"}, + + /* Second path begins with a single backslash */ + {"C:a\b", "\1", "C:a\b\1"}, + {"C:\a\b", "\1", "C:\a\b\1"}, + {"\a\b", "\1", "\a\b\1"}, + {"\\a\b", "\1", "\\a\b\1"}, + {"\\a\b\c", "\1", "\\a\b\c\1"}, + {"\\?\UNC\a", "\1", "\\a\1"}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a", "\1", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}a\1"}, + {"\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a", "\1", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\a\1"}, + {"C:\a\b", "\", "C:\a\b"}, + + /* Second path is fully qualified */ + {"X:\", "C:", "C:\"}, + {"X:\", "C:\", "C:\"}, + {"X:\", "\\", "\\"}, + {"X:\", "\\?\C:", "C:\"}, + {"X:\", "\\?\C:\", "C:\"}, + {"X:\", "\\?\UNC\", "\\"}, + {"X:\", "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\", + "\\?\Volume{e51a1864-6f2d-4019-b73d-f4e60e600c26}\"}, + + /* Canonicalization */ + {"C:\a", ".\b", "C:\a\b"}, + {"C:\a", "..\b", "C:\b"}, + + /* Other */ + {"", "", "\"}, + {"a", "b", "a\b"} +}; + +static void test_PathCchAppendEx(void) +{ + WCHAR path1W[PATHCCH_MAX_CCH]; + WCHAR path2W[PATHCCH_MAX_CCH]; + CHAR path1A[PATHCCH_MAX_CCH]; + HRESULT hr; + INT i; + + if (!pPathCchAppendEx) + { + win_skip("PathCchAppendEx() is not available.\n"); + return; + } + + MultiByteToWideChar(CP_ACP, 0, "\a", -1, path1W, ARRAY_SIZE(path1W)); + MultiByteToWideChar(CP_ACP, 0, "\b", -1, path2W, ARRAY_SIZE(path2W)); + hr = pPathCchAppendEx(NULL, ARRAY_SIZE(path1W), path2W, 0); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + + hr = pPathCchAppendEx(path1W, 0, path2W, 0); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + ok(path1W[0] == '\', "expect path1 unchanged\n"); + + hr = pPathCchAppendEx(path1W, PATHCCH_MAX_CCH + 1, path2W, 0); + ok(hr == E_INVALIDARG, "expect hr %#x, got %#x\n", E_INVALIDARG, hr); + ok(path1W[0] == '\', "expect path1 unchanged\n"); + + hr = pPathCchAppendEx(path1W, ARRAY_SIZE(path1W), NULL, 0); + ok(hr == S_OK, "expect hr %#x, got %#x\n", S_OK, hr); + WideCharToMultiByte(CP_ACP, 0, path1W, -1, path1A, ARRAY_SIZE(path1A), NULL, NULL); + ok(!lstrcmpA(path1A, "\a"), "expect \a, got %s\n", path1A); + + hr = pPathCchAppendEx(path1W, PATHCCH_MAX_CCH, path2W, 0); + ok(hr == S_OK, "expect hr %#x, got %#x\n", S_OK, hr); + + for (i = 0; i < ARRAY_SIZE(append_tests); i++) + { + const struct append_test *t = append_tests + i; + + MultiByteToWideChar(CP_ACP, 0, t->path1, -1, path1W, ARRAY_SIZE(path1W)); + MultiByteToWideChar(CP_ACP, 0, t->path2, -1, path2W, ARRAY_SIZE(path2W)); + hr = pPathCchAppendEx(path1W, PATHCCH_MAX_CCH, path2W, 0); + ok(hr == S_OK, "append "%s" "%s" expect hr %#x, got %#x\n", t->path1, t->path2, S_OK, hr); + if (SUCCEEDED(hr)) + { + WideCharToMultiByte(CP_ACP, 0, path1W, -1, path1A, ARRAY_SIZE(path1A), NULL, NULL); + ok(!lstrcmpA(path1A, t->result), "append "%s" "%s" expect result "%s", got "%s"\n", t->path1, + t->path2, t->result, path1A); + } + } +} + static void test_PathCchAddExtension(void) { WCHAR pathW[PATHCCH_MAX_CCH + 1]; @@ -2109,6 +2230,7 @@ START_TEST(path) pPathCchAddBackslash = (void *)GetProcAddress(hmod, "PathCchAddBackslash"); pPathCchAddBackslashEx = (void *)GetProcAddress(hmod, "PathCchAddBackslashEx"); pPathCchAddExtension = (void *)GetProcAddress(hmod, "PathCchAddExtension"); + pPathCchAppendEx = (void *)GetProcAddress(hmod, "PathCchAppendEx"); pPathCchCanonicalize = (void *)GetProcAddress(hmod, "PathCchCanonicalize"); pPathCchCanonicalizeEx = (void *)GetProcAddress(hmod, "PathCchCanonicalizeEx"); pPathCchCombine = (void *)GetProcAddress(hmod, "PathCchCombine"); @@ -2130,6 +2252,7 @@ START_TEST(path) test_PathCchAddBackslash(); test_PathCchAddBackslashEx(); test_PathCchAddExtension(); + test_PathCchAppendEx(); test_PathCchCanonicalize(); test_PathCchCanonicalizeEx(); test_PathCchCombine(); diff --git a/include/pathcch.h b/include/pathcch.h index 205d4a23a3..18f065c4c6 100644 --- a/include/pathcch.h +++ b/include/pathcch.h @@ -31,6 +31,7 @@ HRESULT WINAPI PathAllocCombine(const WCHAR *path1, const WCHAR *path2, DWORD fl HRESULT WINAPI PathCchAddBackslash(WCHAR *path, SIZE_T size); HRESULT WINAPI PathCchAddBackslashEx(WCHAR *path, SIZE_T size, WCHAR **end, SIZE_T *remaining); HRESULT WINAPI PathCchAddExtension(WCHAR *path, SIZE_T size, const WCHAR *extension); +HRESULT WINAPI PathCchAppendEx(WCHAR *path1, SIZE_T size, const WCHAR *path2, DWORD flags); HRESULT WINAPI PathCchCanonicalize(WCHAR *out, SIZE_T size, const WCHAR *in); HRESULT WINAPI PathCchCanonicalizeEx(WCHAR *out, SIZE_T size, const WCHAR *in, DWORD flags); HRESULT WINAPI PathCchCombine(WCHAR *out, SIZE_T size, const WCHAR *path1, const WCHAR *path2);