From b652e93a460b4b822a01382e5992f96f1d805ffe Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Fri, 3 Jun 2022 17:47:54 -0500 Subject: Replace PathCompare with PathCompareCanonicalized. --- src/libs/dutil/WixToolset.DUtil/dutil.vcxproj | 1 + .../dutil/WixToolset.DUtil/dutil.vcxproj.filters | 3 + src/libs/dutil/WixToolset.DUtil/file2utl.cpp | 164 +++++++++++++++++++++ src/libs/dutil/WixToolset.DUtil/fileutil.cpp | 156 -------------------- src/libs/dutil/WixToolset.DUtil/inc/pathutil.h | 9 +- src/libs/dutil/WixToolset.DUtil/path2utl.cpp | 46 +++++- src/libs/dutil/WixToolset.DUtil/pathutil.cpp | 26 ---- src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp | 69 +++++++++ 8 files changed, 286 insertions(+), 188 deletions(-) create mode 100644 src/libs/dutil/WixToolset.DUtil/file2utl.cpp (limited to 'src/libs/dutil') diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj index ba9e801e..8a6f3b13 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj @@ -66,6 +66,7 @@ 4091;4458 + diff --git a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters index 6444b19c..dbbe68f4 100644 --- a/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters +++ b/src/libs/dutil/WixToolset.DUtil/dutil.vcxproj.filters @@ -69,6 +69,9 @@ Source Files + + Source Files + Source Files diff --git a/src/libs/dutil/WixToolset.DUtil/file2utl.cpp b/src/libs/dutil/WixToolset.DUtil/file2utl.cpp new file mode 100644 index 00000000..88f8377c --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/file2utl.cpp @@ -0,0 +1,164 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + + +// Exit macros +#define FileExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_FILEUTIL, x, s, __VA_ARGS__) +#define FileExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_FILEUTIL, p, x, e, s, __VA_ARGS__) +#define FileExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_FILEUTIL, p, x, s, __VA_ARGS__) +#define FileExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_FILEUTIL, e, x, s, __VA_ARGS__) +#define FileExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_FILEUTIL, g, x, s, __VA_ARGS__) + +// constants + +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; +const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; + + +/******************************************************************* + FileExistsAfterRestart - checks that a file exists and will continue + to exist after restart. + +********************************************************************/ +extern "C" BOOL DAPI FileExistsAfterRestart( + __in_z LPCWSTR wzPath, + __out_opt DWORD *pdwAttributes + ) +{ + HRESULT hr = S_OK; + BOOL fExists = FALSE; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + BOOL fPathEqual = FALSE; + + fExists = FileExistsEx(wzPath, pdwAttributes); + if (fExists) + { + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + hr = PathCompareCanonicalized(wzPath, wzRename, &fPathEqual); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + if (fPathEqual) + { + fExists = FALSE; + break; + } + } + } + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return fExists; +} + + +/******************************************************************* + FileRemoveFromPendingRename - removes the file path from the pending + file rename list. + +********************************************************************/ +extern "C" HRESULT DAPI FileRemoveFromPendingRename( + __in_z LPCWSTR wzPath + ) +{ + HRESULT hr = S_OK; + HKEY hkPendingFileRename = NULL; + LPWSTR* rgsczRenames = NULL; + DWORD cRenames = 0; + BOOL fPathEqual = FALSE; + BOOL fRemoved = FALSE; + DWORD cNewRenames = 0; + + hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to open pending file rename registry key."); + + hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); + if (E_FILENOTFOUND == hr) + { + ExitFunction1(hr = S_OK); + } + FileExitOnFailure(hr, "Failed to read pending file renames."); + + // The pending file renames array is pairs of source and target paths. We only care + // about checking the source paths so skip the target paths (i += 2). + for (DWORD i = 0; i < cRenames; i += 2) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename && *wzRename) + { + hr = PathCompareCanonicalized(wzPath, wzRename, &fPathEqual); + FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); + + // If we find our path in the list, null out the source and target slot and + // we'll compact the array next. + if (fPathEqual) + { + ReleaseNullStr(rgsczRenames[i]); + ReleaseNullStr(rgsczRenames[i + 1]); + fRemoved = TRUE; + } + } + } + + if (fRemoved) + { + // Compact the array by removing any nulls. + for (DWORD i = 0; i < cRenames; ++i) + { + LPWSTR wzRename = rgsczRenames[i]; + if (wzRename) + { + rgsczRenames[cNewRenames] = wzRename; + ++cNewRenames; + } + } + + cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. + + // Write the new array back to the pending file rename key. + hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); + FileExitOnFailure(hr, "Failed to update pending file renames."); + } + +LExit: + ReleaseStrArray(rgsczRenames, cRenames); + ReleaseRegKey(hkPendingFileRename); + + return hr; +} diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp index 2fe04de1..9f68ee52 100644 --- a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp @@ -22,9 +22,6 @@ const BYTE UTF8BOM[] = {0xEF, 0xBB, 0xBF}; const BYTE UTF16BOM[] = {0xFF, 0xFE}; -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_KEY = L"SYSTEM\\CurrentControlSet\\Control\\Session Manager"; -const LPCWSTR REGISTRY_PENDING_FILE_RENAME_VALUE = L"PendingFileRenameOperations"; - /******************************************************************* FileStripExtension - Strip extension from filename @@ -526,159 +523,6 @@ extern "C" BOOL DAPI FileExistsEx( } -/******************************************************************* - FileExistsAfterRestart - checks that a file exists and will continue - to exist after restart. - -********************************************************************/ -extern "C" BOOL DAPI FileExistsAfterRestart( - __in_z LPCWSTR wzPath, - __out_opt DWORD *pdwAttributes - ) -{ - HRESULT hr = S_OK; - BOOL fExists = FALSE; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - - fExists = FileExistsEx(wzPath, pdwAttributes); - if (fExists) - { - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - if (CSTR_EQUAL == nCompare) - { - fExists = FALSE; - break; - } - } - } - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return fExists; -} - - -/******************************************************************* - FileRemoveFromPendingRename - removes the file path from the pending - file rename list. - -********************************************************************/ -extern "C" HRESULT DAPI FileRemoveFromPendingRename( - __in_z LPCWSTR wzPath - ) -{ - HRESULT hr = S_OK; - HKEY hkPendingFileRename = NULL; - LPWSTR* rgsczRenames = NULL; - DWORD cRenames = 0; - int nCompare = 0; - BOOL fRemoved = FALSE; - DWORD cNewRenames = 0; - - hr = RegOpen(HKEY_LOCAL_MACHINE, REGISTRY_PENDING_FILE_RENAME_KEY, KEY_QUERY_VALUE | KEY_SET_VALUE, &hkPendingFileRename); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to open pending file rename registry key."); - - hr = RegReadStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, &rgsczRenames, &cRenames); - if (E_FILENOTFOUND == hr) - { - ExitFunction1(hr = S_OK); - } - FileExitOnFailure(hr, "Failed to read pending file renames."); - - // The pending file renames array is pairs of source and target paths. We only care - // about checking the source paths so skip the target paths (i += 2). - for (DWORD i = 0; i < cRenames; i += 2) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename && *wzRename) - { - // Skip the long path designator if present. - if (L'\\' == wzRename[0] && L'?' == wzRename[1] && L'?' == wzRename[2] && L'\\' == wzRename[3]) - { - wzRename += 4; - } - - hr = PathCompare(wzPath, wzRename, &nCompare); - FileExitOnFailure(hr, "Failed to compare path from pending file rename to check path."); - - // If we find our path in the list, null out the source and target slot and - // we'll compact the array next. - if (CSTR_EQUAL == nCompare) - { - ReleaseNullStr(rgsczRenames[i]); - ReleaseNullStr(rgsczRenames[i + 1]); - fRemoved = TRUE; - } - } - } - - if (fRemoved) - { - // Compact the array by removing any nulls. - for (DWORD i = 0; i < cRenames; ++i) - { - LPWSTR wzRename = rgsczRenames[i]; - if (wzRename) - { - rgsczRenames[cNewRenames] = wzRename; - ++cNewRenames; - } - } - - cRenames = cNewRenames; // ignore the pointers on the end of the array since an early index points to them already. - - // Write the new array back to the pending file rename key. - hr = RegWriteStringArray(hkPendingFileRename, REGISTRY_PENDING_FILE_RENAME_VALUE, rgsczRenames, cRenames); - FileExitOnFailure(hr, "Failed to update pending file renames."); - } - -LExit: - ReleaseStrArray(rgsczRenames, cRenames); - ReleaseRegKey(hkPendingFileRename); - - return hr; -} - - /******************************************************************* FileRead - read a file into memory diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h index 871e706b..e64c8ef3 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h @@ -258,13 +258,14 @@ DAPI_(HRESULT) PathConcatRelativeToBase( ); /******************************************************************* - PathCompare - compares the fully expanded path of the two paths using - ::CompareStringW(). + PathCompareCanonicalized - canonicalizes the two paths using PathCanonicalizeForComparison + which does not resolve relative paths into fully qualified paths. + The strings are then compared using ::CompareStringW(). *******************************************************************/ -DAPI_(HRESULT) PathCompare( +DAPI_(HRESULT) PathCompareCanonicalized( __in_z LPCWSTR wzPath1, __in_z LPCWSTR wzPath2, - __out int* pnResult + __out BOOL* pfEqual ); /******************************************************************* diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp index 61c1803a..1957a8c5 100644 --- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp +++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp @@ -118,11 +118,19 @@ DAPI_(HRESULT) PathCanonicalizeForComparison( PathExitOnFailure(hr, "Failed to backslash terminate the canonicalized path"); } - if ((PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags) && - PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix) + if (PathIsFullyQualified(*psczCanonicalized, &fHasPrefix) && !fHasPrefix && + (PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX & dwCanonicalizeFlags)) { hr = PathPrefix(psczCanonicalized); PathExitOnFailure(hr, "Failed to ensure the long path prefix on the canonicalized path"); + + fHasPrefix = TRUE; + } + + if (fHasPrefix) + { + // Canonicalize \??\ into \\?\. + (*psczCanonicalized)[1] = L'\\'; } LExit: @@ -171,6 +179,40 @@ LExit: return hr; } +DAPI_(HRESULT) PathCompareCanonicalized( + __in_z LPCWSTR wzPath1, + __in_z LPCWSTR wzPath2, + __out BOOL* pfEqual + ) +{ + HRESULT hr = S_OK; + LPWSTR sczCanonicalized1 = NULL; + LPWSTR sczCanonicalized2 = NULL; + DWORD dwDefaultFlags = PATH_CANONICALIZE_APPEND_LONG_PATH_PREFIX | PATH_CANONICALIZE_KEEP_UNC_ROOT; + int nResult = 0; + + if (!wzPath1 || !wzPath2) + { + PathExitWithRootFailure(hr, E_INVALIDARG, "Both paths are required."); + } + + hr = PathCanonicalizeForComparison(wzPath1, dwDefaultFlags, &sczCanonicalized1); + PathExitOnFailure(hr, "Failed to canonicalize wzPath1."); + + hr = PathCanonicalizeForComparison(wzPath2, dwDefaultFlags, &sczCanonicalized2); + PathExitOnFailure(hr, "Failed to canonicalize wzPath2."); + + nResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczCanonicalized1, -1, sczCanonicalized2, -1); + PathExitOnNullWithLastError(nResult, hr, "Failed to compare canonicalized paths."); + + *pfEqual = CSTR_EQUAL == nResult; + +LExit: + ReleaseStr(sczCanonicalized1); + ReleaseStr(sczCanonicalized2); + return hr; +} + DAPI_(HRESULT) PathDirectoryContainsPath( __in_z LPCWSTR wzDirectory, __in_z LPCWSTR wzPath diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp index a9a19b9f..abbf4d4b 100644 --- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp @@ -1080,32 +1080,6 @@ LExit: } -DAPI_(HRESULT) PathCompare( - __in_z LPCWSTR wzPath1, - __in_z LPCWSTR wzPath2, - __out int* pnResult - ) -{ - HRESULT hr = S_OK; - LPWSTR sczPath1 = NULL; - LPWSTR sczPath2 = NULL; - - hr = PathExpand(&sczPath1, wzPath1, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path1."); - - hr = PathExpand(&sczPath2, wzPath2, PATH_EXPAND_ENVIRONMENT | PATH_EXPAND_FULLPATH); - PathExitOnFailure(hr, "Failed to expand path2."); - - *pnResult = ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, sczPath1, -1, sczPath2, -1); - -LExit: - ReleaseStr(sczPath2); - ReleaseStr(sczPath1); - - return hr; -} - - DAPI_(HRESULT) PathCompress( __in_z LPCWSTR wzPath ) diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp index 554c6f00..2505c6bf 100644 --- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp @@ -151,6 +151,10 @@ namespace DutilTests NativeAssert::Succeeded(hr, "Failed to canonicalize path"); NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized); + hr = PathCanonicalizeForComparison(L"\\??\\UNC\\server\\share\\dir", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\dir", sczCanonicalized); + hr = PathCanonicalizeForComparison(L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); NativeAssert::Succeeded(hr, "Failed to canonicalize path"); NativeAssert::StringEqual(L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", sczCanonicalized); @@ -203,6 +207,10 @@ namespace DutilTests NativeAssert::Succeeded(hr, "Failed to canonicalize path"); NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized); + hr = PathCanonicalizeForComparison(L"\\??\\test\\..\\invalidlongpath.exe", 0, &sczCanonicalized); + NativeAssert::Succeeded(hr, "Failed to canonicalize path"); + NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized); + hr = PathCanonicalizeForComparison(L"C:\\.\\invalid:pathchars?.exe", 0, &sczCanonicalized); NativeAssert::Succeeded(hr, "Failed to canonicalize path"); NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized); @@ -263,6 +271,67 @@ namespace DutilTests } } + [Fact] + void PathCompareCanonicalizeEqualTest() + { + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + BOOL fEqual = FALSE; + LPCWSTR rgwzPaths[14] = + { + L"C:\\simplepath", L"C:\\simplepath", + L"\\\\server\\share\\dir\\dir2\\..\\otherdir\\unc.exe", L"\\\\server\\share\\dir\\otherdir\\unc.exe", + L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", L"\\\\server\\share\\otherdir\\unc.exe", + L"\\\\?\\UNC\\server\\share\\..\\..\\otherdir\\unc.exe", L"\\\\?\\UNC\\server\\share\\otherdir\\unc.exe", + L"C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", L"C:\\otherdir\\pastroot.exe", + L"\\\\?\\C:\\dir\\subdir\\..\\..\\..\\otherdir\\pastroot.exe", L"C:\\..\\otherdir\\pastroot.exe", + L"\\??\\C:\\dir", L"\\\\?\\C:\\dir", + }; + + try + { + for (DWORD i = 0; i < countof(rgwzPaths); i += 2) + { + hr = PathCompareCanonicalized(rgwzPaths[i], rgwzPaths[i + 1], &fEqual); + NativeAssert::Succeeded(hr, "PathCompareCanonicalized: {0}, {1}", rgwzPaths[i], rgwzPaths[i + 1]); + Assert::True(fEqual, String::Format("PathCompareCanonicalized: {0}, {1}", gcnew String(rgwzPaths[i]), gcnew String(rgwzPaths[i + 1]))); + } + } + finally + { + ReleaseStr(sczPath); + } + } + + [Fact] + void PathCompareCanonicalizeNotEqualTest() + { + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + BOOL fEqual = FALSE; + LPCWSTR rgwzPaths[8] = + { + L"C:\\simplepath", L"D:\\simplepath", + L"\\\\.\\share\\otherdir\\unc.exe", L"\\\\share\\otherdir\\unc.exe", + L"\\\\server\\.\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", + L"\\\\server\\\\otherdir\\unc.exe", L"\\\\server\\otherdir\\unc.exe", + }; + + try + { + for (DWORD i = 0; i < countof(rgwzPaths); i += 2) + { + hr = PathCompareCanonicalized(rgwzPaths[i], rgwzPaths[i + 1], &fEqual); + NativeAssert::Succeeded(hr, "PathCompareCanonicalized: {0}, {1}", rgwzPaths[i], rgwzPaths[i + 1]); + Assert::False(fEqual, String::Format("PathCompareCanonicalized: {0}, {1}", gcnew String(rgwzPaths[i]), gcnew String(rgwzPaths[i + 1]))); + } + } + finally + { + ReleaseStr(sczPath); + } + } + [Fact] void PathConcatRelativeToBaseTest() { -- cgit v1.2.3-55-g6feb