aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-06-24 12:28:27 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-06-27 11:14:21 -0500
commiteb53852d7ae6838e54525eb57df1d8ce8a722f9b (patch)
tree7fa05bd6df1bce2e20d87c5fbacc1c658dc000aa
parent6ee12a64cb75097a238e60d4fd0ea542e8312214 (diff)
downloadwix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.tar.gz
wix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.tar.bz2
wix-eb53852d7ae6838e54525eb57df1d8ce8a722f9b.zip
Add longPathAware to Burn manifest to support long paths.
Fixes 3455
-rw-r--r--src/burn/engine/approvedexe.cpp9
-rw-r--r--src/burn/engine/cache.cpp6
-rw-r--r--src/burn/engine/engine.cpp2
-rw-r--r--src/burn/engine/exeengine.cpp7
-rw-r--r--src/burn/engine/logging.cpp18
-rw-r--r--src/burn/engine/msuengine.cpp9
-rw-r--r--src/burn/engine/userexperience.cpp5
-rw-r--r--src/burn/engine/variable.cpp75
-rw-r--r--src/burn/test/BurnUnitTest/RegistrationTest.cpp6
-rw-r--r--src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs288
-rw-r--r--src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs4
-rw-r--r--src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs2
-rw-r--r--src/libs/dutil/WixToolset.DUtil/apputil.cpp28
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabcutil.cpp86
-rw-r--r--src/libs/dutil/WixToolset.DUtil/cabutil.cpp68
-rw-r--r--src/libs/dutil/WixToolset.DUtil/dirutil.cpp64
-rw-r--r--src/libs/dutil/WixToolset.DUtil/fileutil.cpp12
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/dirutil.h3
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/pathutil.h67
-rw-r--r--src/libs/dutil/WixToolset.DUtil/inc/shelutil.h15
-rw-r--r--src/libs/dutil/WixToolset.DUtil/logutil.cpp19
-rw-r--r--src/libs/dutil/WixToolset.DUtil/path2utl.cpp145
-rw-r--r--src/libs/dutil/WixToolset.DUtil/pathutil.cpp404
-rw-r--r--src/libs/dutil/WixToolset.DUtil/rexutil.cpp77
-rw-r--r--src/libs/dutil/WixToolset.DUtil/sceutil.cpp11
-rw-r--r--src/libs/dutil/WixToolset.DUtil/shelutil.cpp210
-rw-r--r--src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp182
-rw-r--r--src/test/burn/Directory.wixproj.targets1
-rw-r--r--src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wixproj19
-rw-r--r--src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wxs10
-rw-r--r--src/test/burn/TestData/LongPathTests/PackageA/PackageA.wixproj10
-rw-r--r--src/test/burn/TestData/Templates/Bundle.wxs5
-rw-r--r--src/test/burn/TestExe/Task.cs67
-rw-r--r--src/test/burn/WixTestTools/BundleInstaller.cs4
-rw-r--r--src/test/burn/WixTestTools/MSIExec.cs14
-rw-r--r--src/test/burn/WixTestTools/TestTool.cs2
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs298
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/testhost.longpathaware.manifest11
-rw-r--r--src/test/burn/test_burn.cmd5
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs10
41 files changed, 1890 insertions, 392 deletions
diff --git a/src/burn/engine/approvedexe.cpp b/src/burn/engine/approvedexe.cpp
index 2a96868e..d8bd956b 100644
--- a/src/burn/engine/approvedexe.cpp
+++ b/src/burn/engine/approvedexe.cpp
@@ -148,6 +148,7 @@ extern "C" HRESULT ApprovedExesLaunch(
148 LPWSTR sczCommand = NULL; 148 LPWSTR sczCommand = NULL;
149 LPWSTR sczCommandObfuscated = NULL; 149 LPWSTR sczCommandObfuscated = NULL;
150 LPWSTR sczExecutableDirectory = NULL; 150 LPWSTR sczExecutableDirectory = NULL;
151 size_t cchExecutableDirectory = 0;
151 STARTUPINFOW si = { }; 152 STARTUPINFOW si = { };
152 PROCESS_INFORMATION pi = { }; 153 PROCESS_INFORMATION pi = { };
153 154
@@ -177,9 +178,17 @@ extern "C" HRESULT ApprovedExesLaunch(
177 // Try to get the directory of the executable so we can set the current directory of the process to help those executables 178 // Try to get the directory of the executable so we can set the current directory of the process to help those executables
178 // that expect stuff to be relative to them. Best effort only. 179 // that expect stuff to be relative to them. Best effort only.
179 hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); 180 hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
181 if (SUCCEEDED(hr))
182 {
183 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
184 hr = ::StringCchLengthW(sczExecutableDirectory, MAX_PATH - 1, &cchExecutableDirectory);
185 }
186
180 if (FAILED(hr)) 187 if (FAILED(hr))
181 { 188 {
182 ReleaseNullStr(sczExecutableDirectory); 189 ReleaseNullStr(sczExecutableDirectory);
190
191 hr = S_OK;
183 } 192 }
184 193
185 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); 194 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
diff --git a/src/burn/engine/cache.cpp b/src/burn/engine/cache.cpp
index cf9de1c3..eb5cc508 100644
--- a/src/burn/engine/cache.cpp
+++ b/src/burn/engine/cache.cpp
@@ -174,7 +174,7 @@ extern "C" HRESULT CacheInitialize(
174 174
175 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads. 175 // Cache paths are initialized once so they cannot be changed while the engine is caching payloads.
176 // Always construct the default machine package cache path so we can determine if we're redirected. 176 // Always construct the default machine package cache path so we can determine if we're redirected.
177 hr = PathGetKnownFolder(CSIDL_COMMON_APPDATA, &sczAppData); 177 hr = ShelGetFolder(&sczAppData, CSIDL_COMMON_APPDATA);
178 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine"); 178 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-machine");
179 179
180 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultMachinePackageCache); 180 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultMachinePackageCache);
@@ -210,7 +210,7 @@ extern "C" HRESULT CacheInitialize(
210 pCache->fCustomMachinePackageCache = !fPathEqual; 210 pCache->fCustomMachinePackageCache = !fPathEqual;
211 211
212 212
213 hr = PathGetKnownFolder(CSIDL_LOCAL_APPDATA, &sczAppData); 213 hr = ShelGetFolder(&sczAppData, CSIDL_LOCAL_APPDATA);
214 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user"); 214 ExitOnFailure(hr, "Failed to find local %hs appdata directory.", "per-user");
215 215
216 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultUserPackageCache); 216 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultUserPackageCache);
@@ -1469,7 +1469,7 @@ static HRESULT CalculateWorkingFolders(
1469 HRESULT hr = S_OK; 1469 HRESULT hr = S_OK;
1470 LPWSTR sczBaseAcquisitionPath = NULL; 1470 LPWSTR sczBaseAcquisitionPath = NULL;
1471 1471
1472 hr = PathGetTempPath(&sczBaseAcquisitionPath); 1472 hr = PathGetTempPath(&sczBaseAcquisitionPath, NULL);
1473 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base."); 1473 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base.");
1474 1474
1475 hr = PathBackslashTerminate(&sczBaseAcquisitionPath); 1475 hr = PathBackslashTerminate(&sczBaseAcquisitionPath);
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp
index 7fca1141..13075497 100644
--- a/src/burn/engine/engine.cpp
+++ b/src/burn/engine/engine.cpp
@@ -486,7 +486,7 @@ static HRESULT RunUntrusted(
486 486
487 si.cb = sizeof(si); 487 si.cb = sizeof(si);
488 si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow); 488 si.wShowWindow = static_cast<WORD>(pEngineState->command.nCmdShow);
489 if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, 0, NULL, &si, &pi)) 489 if (!::CreateProcessW(wzCleanRoomBundlePath, sczFullCommandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
490 { 490 {
491 ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine); 491 ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
492 } 492 }
diff --git a/src/burn/engine/exeengine.cpp b/src/burn/engine/exeengine.cpp
index f7be082d..701adb74 100644
--- a/src/burn/engine/exeengine.cpp
+++ b/src/burn/engine/exeengine.cpp
@@ -695,6 +695,7 @@ extern "C" HRESULT ExeEngineRunProcess(
695 BOOL fDelayedCancel = FALSE; 695 BOOL fDelayedCancel = FALSE;
696 BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget; 696 BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget;
697 BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type; 697 BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type;
698 size_t cchCachedDirectory = 0;
698 699
699 // Always add user supplied arguments last. 700 // Always add user supplied arguments last.
700 if (wzUserArgs) 701 if (wzUserArgs)
@@ -703,6 +704,12 @@ extern "C" HRESULT ExeEngineRunProcess(
703 ExitOnFailure(hr, "Failed to append user args."); 704 ExitOnFailure(hr, "Failed to append user args.");
704 } 705 }
705 706
707 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
708 if (wzCachedDirectory && FAILED(::StringCchLengthW(wzCachedDirectory, MAX_PATH - 1, &cchCachedDirectory)))
709 {
710 wzCachedDirectory = NULL;
711 }
712
706 // Make the cache location of the executable the current directory to help those executables 713 // Make the cache location of the executable the current directory to help those executables
707 // that expect stuff to be relative to them. 714 // that expect stuff to be relative to them.
708 si.cb = sizeof(si); 715 si.cb = sizeof(si);
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
index 1020d01f..68f0c35b 100644
--- a/src/burn/engine/logging.cpp
+++ b/src/burn/engine/logging.cpp
@@ -211,7 +211,7 @@ extern "C" void LoggingOpenFailed()
211 LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE); 211 LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE);
212 WORD wNumStrings = 1; 212 WORD wNumStrings = 1;
213 213
214 hr = LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); 214 hr = LogOpen(NULL, L"Setup", L"_Failed", L"log", FALSE, FALSE, NULL);
215 if (SUCCEEDED(hr)) 215 if (SUCCEEDED(hr))
216 { 216 {
217 ExitFunction(); 217 ExitFunction();
@@ -965,19 +965,14 @@ static HRESULT GetNonSessionSpecificTempFolder(
965 ) 965 )
966{ 966{
967 HRESULT hr = S_OK; 967 HRESULT hr = S_OK;
968 WCHAR wzTempFolder[MAX_PATH] = { }; 968 LPWSTR sczTempFolder = NULL;
969 SIZE_T cchTempFolder = 0; 969 SIZE_T cchTempFolder = 0;
970 DWORD dwSessionId = 0; 970 DWORD dwSessionId = 0;
971 LPWSTR sczSessionId = 0; 971 LPWSTR sczSessionId = 0;
972 SIZE_T cchSessionId = 0; 972 SIZE_T cchSessionId = 0;
973 973
974 if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder)) 974 hr = PathGetTempPath(&sczTempFolder, &cchTempFolder);
975 { 975 ExitOnFailure(hr, "Failed to get temp folder.");
976 ExitWithLastError(hr, "Failed to get temp folder.");
977 }
978
979 hr = ::StringCchLengthW(wzTempFolder, countof(wzTempFolder), reinterpret_cast<size_t*>(&cchTempFolder));
980 ExitOnFailure(hr, "Failed to get length of temp folder.");
981 976
982 // If our session id is in the TEMP path then remove that part so we get the non-session 977 // If our session id is in the TEMP path then remove that part so we get the non-session
983 // specific temporary folder. 978 // specific temporary folder.
@@ -989,17 +984,18 @@ static HRESULT GetNonSessionSpecificTempFolder(
989 hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId)); 984 hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId));
990 ExitOnFailure(hr, "Failed to get length of session id string."); 985 ExitOnFailure(hr, "Failed to get length of session id string.");
991 986
992 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzTempFolder + cchTempFolder - cchSessionId, static_cast<DWORD>(cchSessionId), sczSessionId, static_cast<DWORD>(cchSessionId))) 987 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, sczTempFolder + cchTempFolder - cchSessionId, static_cast<DWORD>(cchSessionId), sczSessionId, static_cast<DWORD>(cchSessionId)))
993 { 988 {
994 cchTempFolder -= cchSessionId; 989 cchTempFolder -= cchSessionId;
995 } 990 }
996 } 991 }
997 992
998 hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder); 993 hr = StrAllocString(psczNonSessionTempFolder, sczTempFolder, cchTempFolder);
999 ExitOnFailure(hr, "Failed to copy temp folder."); 994 ExitOnFailure(hr, "Failed to copy temp folder.");
1000 995
1001LExit: 996LExit:
1002 ReleaseStr(sczSessionId); 997 ReleaseStr(sczSessionId);
998 ReleaseStr(sczTempFolder);
1003 999
1004 return hr; 1000 return hr;
1005} 1001}
diff --git a/src/burn/engine/msuengine.cpp b/src/burn/engine/msuengine.cpp
index 1b051165..400fdc92 100644
--- a/src/burn/engine/msuengine.cpp
+++ b/src/burn/engine/msuengine.cpp
@@ -269,7 +269,6 @@ extern "C" HRESULT MsuEngineExecutePackage(
269 HRESULT hr = S_OK; 269 HRESULT hr = S_OK;
270 LPWSTR sczCachedDirectory = NULL; 270 LPWSTR sczCachedDirectory = NULL;
271 LPWSTR sczMsuPath = NULL; 271 LPWSTR sczMsuPath = NULL;
272 LPWSTR sczWindowsPath = NULL;
273 LPWSTR sczSystemPath = NULL; 272 LPWSTR sczSystemPath = NULL;
274 LPWSTR sczWusaPath = NULL; 273 LPWSTR sczWusaPath = NULL;
275 LPWSTR sczCommand = NULL; 274 LPWSTR sczCommand = NULL;
@@ -294,15 +293,12 @@ extern "C" HRESULT MsuEngineExecutePackage(
294 // get wusa.exe path 293 // get wusa.exe path
295 if (fUseSysNativePath) 294 if (fUseSysNativePath)
296 { 295 {
297 hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath); 296 hr = PathSystemWindowsSubdirectory(L"SysNative\\", &sczSystemPath);
298 ExitOnFailure(hr, "Failed to find Windows directory.");
299
300 hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
301 ExitOnFailure(hr, "Failed to append SysNative directory."); 297 ExitOnFailure(hr, "Failed to append SysNative directory.");
302 } 298 }
303 else 299 else
304 { 300 {
305 hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); 301 hr = PathGetSystemDirectory(&sczSystemPath);
306 ExitOnFailure(hr, "Failed to find System32 directory."); 302 ExitOnFailure(hr, "Failed to find System32 directory.");
307 } 303 }
308 304
@@ -390,7 +386,6 @@ LExit:
390 ReleaseStr(sczCachedDirectory); 386 ReleaseStr(sczCachedDirectory);
391 ReleaseStr(sczMsuPath); 387 ReleaseStr(sczMsuPath);
392 ReleaseStr(sczSystemPath); 388 ReleaseStr(sczSystemPath);
393 ReleaseStr(sczWindowsPath);
394 ReleaseStr(sczWusaPath); 389 ReleaseStr(sczWusaPath);
395 ReleaseStr(sczCommand); 390 ReleaseStr(sczCommand);
396 ReleaseStr(sczEscapedKB); 391 ReleaseStr(sczEscapedKB);
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index a97234ef..6f84caba 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -99,6 +99,7 @@ extern "C" HRESULT UserExperienceLoad(
99 HRESULT hr = S_OK; 99 HRESULT hr = S_OK;
100 BOOTSTRAPPER_CREATE_ARGS args = { }; 100 BOOTSTRAPPER_CREATE_ARGS args = { };
101 BOOTSTRAPPER_CREATE_RESULTS results = { }; 101 BOOTSTRAPPER_CREATE_RESULTS results = { };
102 LPCWSTR wzPath = pUserExperience->payloads.rgPayloads[0].sczLocalFilePath;
102 103
103 args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS); 104 args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS);
104 args.pCommand = pCommand; 105 args.pCommand = pCommand;
@@ -109,8 +110,8 @@ extern "C" HRESULT UserExperienceLoad(
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); 110 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110 111
111 // Load BA DLL. 112 // Load BA DLL.
112 pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); 113 pUserExperience->hUXModule = ::LoadLibraryExW(wzPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
113 ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL."); 114 ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL: %ls", wzPath);
114 115
115 // Get BootstrapperApplicationCreate entry-point. 116 // Get BootstrapperApplicationCreate entry-point.
116 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate"); 117 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp
index c96e9d95..8d208a66 100644
--- a/src/burn/engine/variable.cpp
+++ b/src/burn/engine/variable.cpp
@@ -1947,18 +1947,18 @@ static HRESULT InitializeVariableTempFolder(
1947 UNREFERENCED_PARAMETER(dwpData); 1947 UNREFERENCED_PARAMETER(dwpData);
1948 1948
1949 HRESULT hr = S_OK; 1949 HRESULT hr = S_OK;
1950 WCHAR wzPath[MAX_PATH] = { }; 1950 LPWSTR sczPath = NULL;
1951 1951
1952 if (!::GetTempPathW(MAX_PATH, wzPath)) 1952 hr = PathGetTempPath(&sczPath, NULL);
1953 { 1953 ExitOnFailure(hr, "Failed to get temp path.");
1954 ExitWithLastError(hr, "Failed to get temp path.");
1955 }
1956 1954
1957 // set value 1955 // set value
1958 hr = BVariantSetString(pValue, wzPath, 0, FALSE); 1956 hr = BVariantSetString(pValue, sczPath, 0, FALSE);
1959 ExitOnFailure(hr, "Failed to set variant value."); 1957 ExitOnFailure(hr, "Failed to set variant value.");
1960 1958
1961LExit: 1959LExit:
1960 ReleaseStr(sczPath);
1961
1962 return hr; 1962 return hr;
1963} 1963}
1964 1964
@@ -1969,7 +1969,7 @@ static HRESULT InitializeVariableSystemFolder(
1969{ 1969{
1970 HRESULT hr = S_OK; 1970 HRESULT hr = S_OK;
1971 BOOL f64 = (BOOL)dwpData; 1971 BOOL f64 = (BOOL)dwpData;
1972 WCHAR wzSystemFolder[MAX_PATH + 2] = { }; 1972 LPWSTR sczSystemFolder = NULL;
1973 1973
1974#if !defined(_WIN64) 1974#if !defined(_WIN64)
1975 BOOL fIsWow64 = FALSE; 1975 BOOL fIsWow64 = FALSE;
@@ -1979,57 +1979,43 @@ static HRESULT InitializeVariableSystemFolder(
1979 { 1979 {
1980 if (f64) 1980 if (f64)
1981 { 1981 {
1982 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) 1982 hr = PathGetSystemDirectory(&sczSystemFolder);
1983 { 1983 ExitOnFailure(hr, "Failed to get 64-bit system folder.");
1984 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1985 }
1986 } 1984 }
1987 else 1985 else
1988 { 1986 {
1989 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) 1987 hr = PathGetSystemWow64Directory(&sczSystemFolder);
1990 { 1988 ExitOnFailure(hr, "Failed to get 32-bit system folder.");
1991 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1992 }
1993 } 1989 }
1994 } 1990 }
1995 else 1991 else
1996 { 1992 {
1997 if (!f64) 1993 if (!f64)
1998 { 1994 {
1999 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) 1995 hr = PathGetSystemDirectory(&sczSystemFolder);
2000 { 1996 ExitOnFailure(hr, "Failed to get 32-bit system folder.");
2001 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
2002 }
2003 } 1997 }
2004 } 1998 }
2005#else 1999#else
2006 if (f64) 2000 if (f64)
2007 { 2001 {
2008 if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder))) 2002 hr = PathGetSystemDirectory(&sczSystemFolder);
2009 { 2003 ExitOnFailure(hr, "Failed to get 64-bit system folder.");
2010 ExitWithLastError(hr, "Failed to get 64-bit system folder.");
2011 }
2012 } 2004 }
2013 else 2005 else
2014 { 2006 {
2015 if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder))) 2007 hr = PathGetSystemWow64Directory(&sczSystemFolder);
2016 { 2008 ExitOnFailure(hr, "Failed to get 32-bit system folder.");
2017 ExitWithLastError(hr, "Failed to get 32-bit system folder.");
2018 }
2019 } 2009 }
2020#endif 2010#endif
2021 2011
2022 if (*wzSystemFolder)
2023 {
2024 hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder));
2025 ExitOnFailure(hr, "Failed to backslash terminate system folder.");
2026 }
2027
2028 // set value 2012 // set value
2029 hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE); 2013 hr = BVariantSetString(pValue, sczSystemFolder, 0, FALSE);
2030 ExitOnFailure(hr, "Failed to set system folder variant value."); 2014 ExitOnFailure(hr, "Failed to set system folder variant value.");
2031 2015
2032LExit: 2016LExit:
2017 ReleaseStr(sczSystemFolder);
2018
2033 return hr; 2019 return hr;
2034} 2020}
2035 2021
@@ -2041,26 +2027,25 @@ static HRESULT InitializeVariableWindowsVolumeFolder(
2041 UNREFERENCED_PARAMETER(dwpData); 2027 UNREFERENCED_PARAMETER(dwpData);
2042 2028
2043 HRESULT hr = S_OK; 2029 HRESULT hr = S_OK;
2044 WCHAR wzWindowsPath[MAX_PATH] = { }; 2030 LPWSTR sczWindowsPath = NULL;
2045 WCHAR wzVolumePath[MAX_PATH] = { }; 2031 LPWSTR sczVolumePath = NULL;
2046 2032
2047 // get windows directory 2033 // get windows directory
2048 if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath))) 2034 hr = PathSystemWindowsSubdirectory(NULL, &sczWindowsPath);
2049 { 2035 ExitOnFailure(hr, "Failed to get windows directory.");
2050 ExitWithLastError(hr, "Failed to get windows directory.");
2051 }
2052 2036
2053 // get volume path name 2037 // get volume path name
2054 if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH)) 2038 hr = PathGetVolumePathName(sczWindowsPath, &sczVolumePath);
2055 { 2039 ExitOnFailure(hr, "Failed to get volume path name.");
2056 ExitWithLastError(hr, "Failed to get volume path name.");
2057 }
2058 2040
2059 // set value 2041 // set value
2060 hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE); 2042 hr = BVariantSetString(pValue, sczVolumePath, 0, FALSE);
2061 ExitOnFailure(hr, "Failed to set variant value."); 2043 ExitOnFailure(hr, "Failed to set variant value.");
2062 2044
2063LExit: 2045LExit:
2046 ReleaseStr(sczWindowsPath);
2047 ReleaseStr(sczVolumePath);
2048
2064 return hr; 2049 return hr;
2065} 2050}
2066 2051
diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
index 883b9cc8..aa3bd34b 100644
--- a/src/burn/test/BurnUnitTest/RegistrationTest.cpp
+++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
@@ -108,8 +108,8 @@ namespace Bootstrapper
108 TestThrowOnFailure(hr, L"Failed to register bundle."); 108 TestThrowOnFailure(hr, L"Failed to register bundle.");
109 109
110 // verify that registration was created 110 // verify that registration was created
111 Assert::True(Directory::Exists(cacheDirectory)); 111 Assert::True(Directory::Exists(cacheDirectory), "Cache directory didn't exist.");
112 Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe")))); 112 Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))), "Bundle exe wasn't cached.");
113 113
114 this->ValidateUninstallKeyResume(Int32(BURN_RESUME_MODE_ACTIVE)); 114 this->ValidateUninstallKeyResume(Int32(BURN_RESUME_MODE_ACTIVE));
115 this->ValidateRunOnceKeyEntry(cacheExePath); 115 this->ValidateRunOnceKeyEntry(cacheExePath);
@@ -119,7 +119,7 @@ namespace Bootstrapper
119 TestThrowOnFailure(hr, L"Failed to unregister bundle."); 119 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
120 120
121 // verify that registration was removed 121 // verify that registration was removed
122 Assert::False(Directory::Exists(cacheDirectory)); 122 Assert::False(Directory::Exists(cacheDirectory), "Cache directory wasn't removed.");
123 123
124 this->ValidateUninstallKeyNull(L"Resume"); 124 this->ValidateUninstallKeyNull(L"Resume");
125 this->ValidateRunOnceKeyString(TEST_BUNDLE_ID, nullptr); 125 this->ValidateRunOnceKeyString(TEST_BUNDLE_ID, nullptr);
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
index eb07aa13..4a932645 100644
--- a/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
+++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
@@ -2,11 +2,16 @@
2 2
3namespace WixBuildTools.TestSupport 3namespace WixBuildTools.TestSupport
4{ 4{
5 using System;
5 using System.Collections.Concurrent; 6 using System.Collections.Concurrent;
6 using System.Collections.Generic; 7 using System.Collections.Generic;
8 using System.ComponentModel;
7 using System.Diagnostics; 9 using System.Diagnostics;
8 using System.IO; 10 using System.IO;
11 using System.Runtime.InteropServices;
9 using System.Text; 12 using System.Text;
13 using System.Threading.Tasks;
14 using Microsoft.Win32.SafeHandles;
10 15
11 public abstract class ExternalExecutable 16 public abstract class ExternalExecutable
12 { 17 {
@@ -19,6 +24,125 @@ namespace WixBuildTools.TestSupport
19 24
20 protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null) 25 protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
21 { 26 {
27 // https://github.com/dotnet/runtime/issues/58492
28 // Process.Start doesn't currently support starting a process with a long path,
29 // but the way to support long paths doesn't support searching for the executable if it was a relative path.
30 // Avoid the managed way of doing this even if the target isn't a long path to help verify that the native way works.
31 if (!Path.IsPathRooted(this.exePath))
32 {
33 return this.RunManaged(args, mergeErrorIntoOutput, workingDirectory);
34 }
35
36 // https://web.archive.org/web/20150331190801/https://support.microsoft.com/en-us/kb/190351
37 var commandLine = $"\"{this.exePath}\" {args}";
38 var currentDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath);
39 if (String.IsNullOrEmpty(currentDirectory))
40 {
41 currentDirectory = null;
42 }
43 var processInfo = new PROCESS_INFORMATION();
44 var startInfo = new STARTUPINFOW
45 {
46 cb = Marshal.SizeOf(typeof(STARTUPINFOW)),
47 dwFlags = StartupInfoFlags.STARTF_FORCEOFFFEEDBACK | StartupInfoFlags.STARTF_USESTDHANDLES,
48 hStdInput = GetStdHandle(StdHandleType.STD_INPUT_HANDLE),
49 };
50 SafeFileHandle hStdOutputParent = null;
51 SafeFileHandle hStdErrorParent = null;
52
53 try
54 {
55 CreatePipeForProcess(out hStdOutputParent, out startInfo.hStdOutput);
56
57 if (!mergeErrorIntoOutput)
58 {
59 CreatePipeForProcess(out hStdErrorParent, out startInfo.hStdError);
60 }
61 else
62 {
63 if (!DuplicateHandle(GetCurrentProcess(), startInfo.hStdOutput, GetCurrentProcess(), out startInfo.hStdError, 0, true, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
64 {
65 throw new Win32Exception();
66 }
67 }
68
69 if (!CreateProcessW(this.exePath, commandLine, IntPtr.Zero, IntPtr.Zero, true, CreateProcessFlags.CREATE_NO_WINDOW, IntPtr.Zero,
70 currentDirectory, ref startInfo, ref processInfo))
71 {
72 throw new Win32Exception();
73 }
74
75 startInfo.Dispose();
76
77 return GetResultFromNative(mergeErrorIntoOutput, hStdOutputParent, hStdErrorParent, processInfo.hProcess, this.exePath, args);
78 }
79 finally
80 {
81 hStdErrorParent?.Dispose();
82 hStdOutputParent?.Dispose();
83
84 startInfo.Dispose();
85 processInfo.Dispose();
86 }
87 }
88
89 private static ExternalExecutableResult GetResultFromNative(bool mergeErrorIntoOutput, SafeFileHandle hStdOutputParent, SafeFileHandle hStdErrorParent, IntPtr hProcess, string fileName, string args)
90 {
91 using (var outputStream = new StreamReader(new FileStream(hStdOutputParent, FileAccess.Read)))
92 using (var errorStream = mergeErrorIntoOutput ? null : new StreamReader(new FileStream(hStdErrorParent, FileAccess.Read)))
93 {
94 var outputTask = Task.Run(() => ReadProcessStreamLines(outputStream));
95 var errorTask = Task.Run(() => ReadProcessStreamLines(errorStream));
96
97 while (!outputTask.Wait(100) || !errorTask.Wait(100)) { Task.Yield(); }
98 var standardOutput = outputTask.Result;
99 var standardError = errorTask.Result;
100
101 if (WaitForSingleObject(hProcess, -1) != 0)
102 {
103 throw new Win32Exception();
104 }
105
106 if (!GetExitCodeProcess(hProcess, out var exitCode))
107 {
108 throw new Win32Exception();
109 }
110
111 return new ExternalExecutableResult
112 {
113 ExitCode = exitCode,
114 StandardError = standardError,
115 StandardOutput = standardOutput,
116 FileName = fileName,
117 Arguments = args,
118 };
119 }
120 }
121
122 private static string[] ReadProcessStreamLines(StreamReader streamReader)
123 {
124 if (streamReader == null)
125 {
126 return null;
127 }
128
129 var lines = new List<string>();
130 while (true)
131 {
132 var line = streamReader.ReadLine();
133 if (line == null)
134 {
135 break;
136 }
137
138 lines.Add(line);
139 }
140
141 return lines.ToArray();
142 }
143
144 protected ExternalExecutableResult RunManaged(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
145 {
22 var startInfo = new ProcessStartInfo(this.exePath, args) 146 var startInfo = new ProcessStartInfo(this.exePath, args)
23 { 147 {
24 CreateNoWindow = true, 148 CreateNoWindow = true,
@@ -48,7 +172,8 @@ namespace WixBuildTools.TestSupport
48 ExitCode = process.ExitCode, 172 ExitCode = process.ExitCode,
49 StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(), 173 StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
50 StandardOutput = standardOutput.ToArray(), 174 StandardOutput = standardOutput.ToArray(),
51 StartInfo = startInfo, 175 FileName = this.exePath,
176 Arguments = args,
52 }; 177 };
53 } 178 }
54 } 179 }
@@ -84,5 +209,166 @@ namespace WixBuildTools.TestSupport
84 209
85 return sb.ToString(); 210 return sb.ToString();
86 } 211 }
212
213 private static void CreatePipeForProcess(out SafeFileHandle hReadPipe, out IntPtr hWritePipe)
214 {
215 var securityAttributes = new SECURITY_ATTRIBUTES
216 {
217 nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES)),
218 bInheritHandle = true,
219 };
220
221 if (!CreatePipe(out var hReadTemp, out hWritePipe, ref securityAttributes, 0))
222 {
223 throw new Win32Exception();
224 }
225
226 // Only the handle passed to the process should be inheritable, so have to duplicate the other handle to get an uninheritable one.
227 if (!DuplicateHandle(GetCurrentProcess(), hReadTemp, GetCurrentProcess(), out var hReadPipePtr, 0, false, DuplicateHandleOptions.DUPLICATE_CLOSE_SOURCE | DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
228 {
229 throw new Win32Exception();
230 }
231
232 hReadPipe = new SafeFileHandle(hReadPipePtr, true);
233 }
234
235 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
236 private extern static IntPtr GetStdHandle(StdHandleType nStdHandle);
237
238 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
239 [return: MarshalAs(UnmanagedType.Bool)]
240 private extern static bool CreatePipe(out IntPtr hReadPipe, out IntPtr hWritePipe, ref SECURITY_ATTRIBUTES lpPipeAttributes, int nSize);
241
242 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
243 [return: MarshalAs(UnmanagedType.Bool)]
244 private extern static bool CreateProcessW(
245 string lpApplicationName,
246 string lpCommandLine,
247 IntPtr lpProcessAttributes,
248 IntPtr lpThreadAttributes,
249 [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
250 CreateProcessFlags dwCreationFlags,
251 IntPtr lpEnvironment,
252 string lpCurrentDirectory,
253 ref STARTUPINFOW lpStartupInfo,
254 ref PROCESS_INFORMATION lpProcessInformation);
255
256 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
257 private extern static IntPtr GetCurrentProcess();
258
259 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
260 [return: MarshalAs(UnmanagedType.Bool)]
261 private extern static bool GetExitCodeProcess(IntPtr hHandle, out int lpExitCode);
262
263 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
264 private extern static int WaitForSingleObject(IntPtr hHandle, int dwMilliseconds);
265
266 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
267 [return: MarshalAs(UnmanagedType.Bool)]
268 private extern static bool CloseHandle(IntPtr hObject);
269
270 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
271 [return: MarshalAs(UnmanagedType.Bool)]
272 private extern static bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, DuplicateHandleOptions dwOptions);
273
274 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
275 private struct SECURITY_ATTRIBUTES
276 {
277 public int nLength;
278 public IntPtr lpSecurityDescriptor;
279 [MarshalAs(UnmanagedType.Bool)]
280 public bool bInheritHandle;
281 }
282
283 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
284 private struct STARTUPINFOW
285 {
286 public int cb;
287 public string lpReserved;
288 public string lpDesktop;
289 public string lpTitle;
290 public int dwX;
291 public int dwY;
292 public int dwXSize;
293 public int dwYSize;
294 public int dwXCountChars;
295 public int dwYCountChars;
296 public int dwFillAttribute;
297 public StartupInfoFlags dwFlags;
298 public short wShowWindow;
299 public short cbReserved2;
300 public IntPtr lpReserved2;
301 public IntPtr hStdInput;
302 public IntPtr hStdOutput;
303 public IntPtr hStdError;
304
305 public void Dispose()
306 {
307 // This makes assumptions based on how it's used above.
308 if (this.hStdError != IntPtr.Zero)
309 {
310 CloseHandle(this.hStdError);
311 this.hStdError = IntPtr.Zero;
312 }
313
314 if (this.hStdOutput != IntPtr.Zero)
315 {
316 CloseHandle(this.hStdOutput);
317 this.hStdOutput = IntPtr.Zero;
318 }
319 }
320 }
321
322 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
323 private struct PROCESS_INFORMATION
324 {
325 public IntPtr hProcess;
326 public IntPtr hThread;
327 public int dwProcessId;
328 public int dwThreadId;
329
330 public void Dispose()
331 {
332 if (this.hProcess != IntPtr.Zero)
333 {
334 CloseHandle(this.hProcess);
335 this.hProcess = IntPtr.Zero;
336 }
337
338 if (this.hThread != IntPtr.Zero)
339 {
340 CloseHandle(this.hThread);
341 this.hThread = IntPtr.Zero;
342 }
343 }
344 }
345
346 private enum StdHandleType
347 {
348 STD_INPUT_HANDLE = -10,
349 STD_OUTPUT_HANDLE = -11,
350 STD_ERROR_HANDLE = -12,
351 }
352
353 [Flags]
354 private enum CreateProcessFlags
355 {
356 None = 0x0,
357 CREATE_NO_WINDOW = 0x08000000,
358 }
359
360 [Flags]
361 private enum StartupInfoFlags
362 {
363 None = 0x0,
364 STARTF_FORCEOFFFEEDBACK = 0x80,
365 STARTF_USESTDHANDLES = 0x100,
366 }
367
368 private enum DuplicateHandleOptions
369 {
370 DUPLICATE_CLOSE_SOURCE = 1,
371 DUPLICATE_SAME_ACCESS = 2,
372 }
87 } 373 }
88} 374}
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
index 19b5183b..950ee4bd 100644
--- a/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
+++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
@@ -12,6 +12,8 @@ namespace WixBuildTools.TestSupport
12 12
13 public string[] StandardOutput { get; set; } 13 public string[] StandardOutput { get; set; }
14 14
15 public ProcessStartInfo StartInfo { get; set; } 15 public string FileName { get; set; }
16
17 public string Arguments { get; set; }
16 } 18 }
17} 19}
diff --git a/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
index 8d670bf0..15b7631d 100644
--- a/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
+++ b/src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
@@ -33,7 +33,7 @@ namespace WixBuildTools.TestSupport
33 RobocopyFolder(sourceDirectoryPath, this.BaseFolder); 33 RobocopyFolder(sourceDirectoryPath, this.BaseFolder);
34 } 34 }
35 35
36 private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath) 36 public static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath)
37 { 37 {
38 var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1"; 38 var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1";
39 return RobocopyRunner.Execute(args); 39 return RobocopyRunner.Execute(args);
diff --git a/src/libs/dutil/WixToolset.DUtil/apputil.cpp b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
index b70c8cfb..9e75082a 100644
--- a/src/libs/dutil/WixToolset.DUtil/apputil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/apputil.cpp
@@ -87,31 +87,27 @@ DAPI_(HRESULT) LoadSystemLibraryWithPath(
87 ) 87 )
88{ 88{
89 HRESULT hr = S_OK; 89 HRESULT hr = S_OK;
90 DWORD cch = 0; 90 LPWSTR sczDirectory = NULL;
91 WCHAR wzPath[MAX_PATH] = { }; 91 LPWSTR sczPath = NULL;
92 92
93 cch = ::GetSystemDirectoryW(wzPath, MAX_PATH); 93 hr = PathGetSystemDirectory(&sczDirectory);
94 AppExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory."); 94 AppExitOnFailure(hr, "Failed to get the Windows system directory.");
95 95
96 if (L'\\' != wzPath[cch - 1]) 96 hr = StrAllocFormatted(&sczPath, L"%ls%ls", sczDirectory, wzModuleName);
97 { 97 AppExitOnFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
98 hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1);
99 AppExitOnRootFailure(hr, "Failed to terminate the string with a backslash.");
100 }
101 98
102 hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName); 99 *phModule = ::LoadLibraryExW(sczPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
103 AppExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName); 100 AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", sczPath);
104
105 *phModule = ::LoadLibraryExW(wzPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
106 AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName);
107 101
108 if (psczPath) 102 if (psczPath)
109 { 103 {
110 hr = StrAllocString(psczPath, wzPath, MAX_PATH); 104 *psczPath = sczPath;
111 AppExitOnFailure(hr, "Failed to copy the path to library."); 105 sczPath = NULL;
112 } 106 }
113 107
114LExit: 108LExit:
109 ReleaseStr(sczDirectory);
110
115 return hr; 111 return hr;
116} 112}
117 113
diff --git a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
index d1edc54d..294669af 100644
--- a/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
@@ -89,8 +89,8 @@ struct CABC_DATA
89 89
90 STRINGDICT_HANDLE shDictHandle; 90 STRINGDICT_HANDLE shDictHandle;
91 91
92 WCHAR wzCabinetPath[MAX_PATH]; 92 LPWSTR sczCabinetPath;
93 WCHAR wzEmptyFile[MAX_PATH]; 93 LPWSTR sczEmptyFile;
94 HANDLE hEmptyFile; 94 HANDLE hEmptyFile;
95 DWORD dwLastFileIndex; 95 DWORD dwLastFileIndex;
96 96
@@ -197,33 +197,17 @@ extern "C" HRESULT DAPI CabCBegin(
197 197
198 HRESULT hr = S_OK; 198 HRESULT hr = S_OK;
199 CABC_DATA *pcd = NULL; 199 CABC_DATA *pcd = NULL;
200 WCHAR wzTempPath[MAX_PATH] = { };
201 200
202 C_ASSERT(sizeof(MSIFILEHASHINFO) == 20); 201 C_ASSERT(sizeof(MSIFILEHASHINFO) == 20);
203 202
204 WCHAR wzPathBuffer [MAX_PATH] = L""; 203 LPWSTR pwzPathBuffer = NULL;
205 size_t cchPathBuffer;
206 if (wzCabDir) 204 if (wzCabDir)
207 { 205 {
208 hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer); 206 hr = StrAllocString(&pwzPathBuffer, wzCabDir, 0);
209 CabcExitOnFailure(hr, "Failed to get length of cab directory");
210
211 // Need room to terminate with L'\\' and L'\0'
212 if((MAX_PATH - 1) <= cchPathBuffer || 0 == cchPathBuffer)
213 {
214 hr = E_INVALIDARG;
215 CabcExitOnFailure(hr, "Cab directory had invalid length: %u", cchPathBuffer);
216 }
217
218 hr = ::StringCchCopyW(wzPathBuffer, countof(wzPathBuffer), wzCabDir);
219 CabcExitOnFailure(hr, "Failed to copy cab directory to buffer"); 207 CabcExitOnFailure(hr, "Failed to copy cab directory to buffer");
220 208
221 if (L'\\' != wzPathBuffer[cchPathBuffer - 1]) 209 hr = PathBackslashTerminate(&pwzPathBuffer);
222 { 210 CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
223 hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\");
224 CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
225 ++cchPathBuffer;
226 }
227 } 211 }
228 212
229 pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE)); 213 pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE));
@@ -290,33 +274,23 @@ extern "C" HRESULT DAPI CabCBegin(
290 CabcExitWithLastError(hr, "failed to convert cab name to multi-byte"); 274 CabcExitWithLastError(hr, "failed to convert cab name to multi-byte");
291 } 275 }
292 276
293 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL)) 277 if (0 == ::WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, pwzPathBuffer, -1, pcd->ccab.szCabPath, sizeof(pcd->ccab.szCab), NULL, NULL))
294 { 278 {
295 CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte"); 279 CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte");
296 } 280 }
297 281
298 // Remember the path to the cabinet. 282 // Remember the path to the cabinet.
299 hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer); 283 hr = PathConcat(pwzPathBuffer, wzCab, &pcd->sczCabinetPath);
300 CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer);
301
302 hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab);
303 CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab); 284 CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab);
304 285
305 // Get the empty file to use as the blank marker for duplicates. 286 // Get the empty file to use as the blank marker for duplicates.
306 if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) 287 hr = DirCreateTempPath(L"WSC", &pcd->sczEmptyFile);
307 { 288 CabcExitOnFailure(hr, "Failed to create a temp file name.");
308 CabcExitWithLastError(hr, "Failed to get temp path.");
309 }
310
311 if (!::GetTempFileNameW(wzTempPath, L"WSC", 0, pcd->wzEmptyFile))
312 {
313 CabcExitWithLastError(hr, "Failed to create a temp file name.");
314 }
315 289
316 // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us) 290 // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us)
317 // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst 291 // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst
318 // case is we'll leave a zero byte file behind in the temp folder. 292 // case is we'll leave a zero byte file behind in the temp folder.
319 pcd->hEmptyFile = ::CreateFileW(pcd->wzEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); 293 pcd->hEmptyFile = ::CreateFileW(pcd->sczEmptyFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
320 294
321 hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE); 295 hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE);
322 CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files"); 296 CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files");
@@ -358,6 +332,8 @@ extern "C" HRESULT DAPI CabCBegin(
358 *phContext = pcd; 332 *phContext = pcd;
359 333
360LExit: 334LExit:
335 ReleaseStr(pwzPathBuffer);
336
361 if (FAILED(hr) && pcd && pcd->hfci) 337 if (FAILED(hr) && pcd && pcd->hfci)
362 { 338 {
363 ::FCIDestroy(pcd->hfci); 339 ::FCIDestroy(pcd->hfci);
@@ -527,7 +503,7 @@ extern "C" HRESULT DAPI CabCFinish(
527 // files point at the same path (the empty file) so there is no point in tracking them with 503 // files point at the same path (the empty file) so there is no point in tracking them with
528 // their path. 504 // their path.
529 fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath; 505 fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath;
530 fileInfo.wzEmptyPath = pcd->wzEmptyFile; 506 fileInfo.wzEmptyPath = pcd->sczEmptyFile;
531 507
532 // Use the provided token, otherwise default to the source file name. 508 // Use the provided token, otherwise default to the source file name.
533 if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken) 509 if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken)
@@ -643,7 +619,7 @@ extern "C" HRESULT DAPI CabCFinish(
643 if (pcd->fGoodCab && pcd->cDuplicates) 619 if (pcd->fGoodCab && pcd->cDuplicates)
644 { 620 {
645 hr = UpdateDuplicateFiles(pcd); 621 hr = UpdateDuplicateFiles(pcd);
646 CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->wzCabinetPath); 622 CabcExitOnFailure(hr, "Failed to update duplicates in cabinet: %ls", pcd->sczCabinetPath);
647 } 623 }
648 624
649LExit: 625LExit:
@@ -692,6 +668,9 @@ static void FreeCabCData(
692 ReleaseMem(pcd->prgFiles); 668 ReleaseMem(pcd->prgFiles);
693 ReleaseMem(pcd->prgDuplicates); 669 ReleaseMem(pcd->prgDuplicates);
694 670
671 ReleaseStr(pcd->sczCabinetPath);
672 ReleaseStr(pcd->sczEmptyFile);
673
695 ReleaseMem(pcd); 674 ReleaseMem(pcd);
696 } 675 }
697} 676}
@@ -709,7 +688,7 @@ static HRESULT CheckForDuplicateFile(
709 __in LONGLONG llFileSize 688 __in LONGLONG llFileSize
710 ) 689 )
711{ 690{
712 DWORD i; 691 DWORD i = 0;
713 HRESULT hr = S_OK; 692 HRESULT hr = S_OK;
714 UINT er = ERROR_SUCCESS; 693 UINT er = ERROR_SUCCESS;
715 694
@@ -916,17 +895,17 @@ static HRESULT UpdateDuplicateFiles(
916 LPVOID pv = NULL; 895 LPVOID pv = NULL;
917 MS_CABINET_HEADER *pCabinetHeader = NULL; 896 MS_CABINET_HEADER *pCabinetHeader = NULL;
918 897
919 hCabinet = ::CreateFileW(pcd->wzCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 898 hCabinet = ::CreateFileW(pcd->sczCabinetPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
920 if (INVALID_HANDLE_VALUE == hCabinet) 899 if (INVALID_HANDLE_VALUE == hCabinet)
921 { 900 {
922 CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath); 901 CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->sczCabinetPath);
923 } 902 }
924 903
925 // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as 904 // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as
926 // the upper bound for the memory map. 905 // the upper bound for the memory map.
927 if (!::GetFileSizeEx(hCabinet, &liCabinetSize)) 906 if (!::GetFileSizeEx(hCabinet, &liCabinetSize))
928 { 907 {
929 CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->wzCabinetPath); 908 CabcExitWithLastError(hr, "Failed to get size of cabinet: %ls", pcd->sczCabinetPath);
930 } 909 }
931 910
932 if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE) 911 if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE)
@@ -942,11 +921,11 @@ static HRESULT UpdateDuplicateFiles(
942 hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL); 921 hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL);
943 if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping) 922 if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping)
944 { 923 {
945 CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->wzCabinetPath); 924 CabcExitWithLastError(hr, "Failed to memory map cabinet file: %ls", pcd->sczCabinetPath);
946 } 925 }
947 926
948 pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0); 927 pv = ::MapViewOfFile(hCabinetMapping, FILE_MAP_WRITE, 0, 0, 0);
949 CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->wzCabinetPath); 928 CabcExitOnNullWithLastError(pv, hr, "Failed to map view of cabinet file: %ls", pcd->sczCabinetPath);
950 929
951 pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv); 930 pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv);
952 931
@@ -1155,7 +1134,7 @@ static __callback INT_PTR DIAMONDAPI CabCOpen(
1155 1134
1156 if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile)) 1135 if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
1157 { 1136 {
1158 CabcExitOnLastError(hr, "failed to open file: %s", pszFile); 1137 CabcExitOnLastError(hr, "failed to open file: %hs", pszFile);
1159 } 1138 }
1160 1139
1161LExit: 1140LExit:
@@ -1326,11 +1305,12 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile(
1326 1305
1327 HRESULT hr = S_OK; 1306 HRESULT hr = S_OK;
1328 char szTempPath[MAX_PATH] = { }; 1307 char szTempPath[MAX_PATH] = { };
1329 DWORD cchTempPath = MAX_PATH;
1330 DWORD dwProcessId = ::GetCurrentProcessId(); 1308 DWORD dwProcessId = ::GetCurrentProcessId();
1331 HANDLE hTempFile = INVALID_HANDLE_VALUE; 1309 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1332 1310
1333 if (MAX_PATH < ::GetTempPathA(cchTempPath, szTempPath)) 1311 // TODO: Allow user to pass in different temp path in case the default is too long,
1312 // and/or see if magic similar to CABC_MAGIC_UNICODE_STRING_MARKER can be used to pass ourselves a path longer than MAX_PATH.
1313 if (MAX_PATH < ::GetTempPathA(countof(szTempPath), szTempPath))
1334 { 1314 {
1335 CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation."); 1315 CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation.");
1336 } 1316 }
@@ -1339,7 +1319,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile(
1339 { 1319 {
1340 LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex)); 1320 LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex));
1341 1321
1342 hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId); 1322 hr = ::StringCbPrintfA(szFile, cbFile, "%hs\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1343 CabcExitOnFailure(hr, "failed to format log file path."); 1323 CabcExitOnFailure(hr, "failed to format log file path.");
1344 1324
1345 hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); 1325 hTempFile = ::CreateFileA(szFile, 0, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
@@ -1351,7 +1331,11 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile(
1351 } 1331 }
1352 else 1332 else
1353 { 1333 {
1354 hr = E_FAIL; // this file was taken so be pessimistic and assume we're not going to find one. 1334 hr = HRESULT_FROM_WIN32(::GetLastError()); // this file was taken so be pessimistic and assume we're not going to find one.
1335 if (SUCCEEDED(hr))
1336 {
1337 hr = E_FAIL;
1338 }
1355 } 1339 }
1356 } 1340 }
1357 CabcExitOnFailure(hr, "failed to find temporary file."); 1341 CabcExitOnFailure(hr, "failed to find temporary file.");
@@ -1386,7 +1370,7 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet(
1386 if (pccab->iCab == 1) 1370 if (pccab->iCab == 1)
1387 { 1371 {
1388 pcd->wzFirstCabinetName[0] = '\0'; 1372 pcd->wzFirstCabinetName[0] = '\0';
1389 LPCWSTR pwzCabinetName = PathFile(pcd->wzCabinetPath); 1373 LPCWSTR pwzCabinetName = PathFile(pcd->sczCabinetPath);
1390 size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName)); 1374 size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName));
1391 if (len > 4) 1375 if (len > 4)
1392 { 1376 {
diff --git a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
index f3629d57..57463e1a 100644
--- a/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/cabutil.cpp
@@ -261,15 +261,15 @@ static HRESULT DAPI CabOperation(
261 ) 261 )
262{ 262{
263 HRESULT hr = S_OK; 263 HRESULT hr = S_OK;
264 BOOL fResult; 264 BOOL fResult = FALSE;
265 265
266 LPWSTR sczCabinet = NULL; 266 LPWSTR sczCabinet = NULL;
267 LPWSTR pwz = NULL; 267 LPWSTR pwz = NULL;
268 CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings 268 LPSTR pszCabDirectory = NULL;
269 CHAR szCabFile[MAX_PATH * 4]; 269 CHAR szCabFile[MAX_PATH * 4] = { }; // Make sure this is big enough for UTF-8 strings
270 270
271 CAB_CALLBACK_STRUCT ccs; 271 CAB_CALLBACK_STRUCT ccs = { };
272 PFNFDINOTIFY pfnFdiNotify; 272 PFNFDINOTIFY pfnFdiNotify = NULL;
273 273
274 // 274 //
275 // ensure the cabinet.dll is loaded 275 // ensure the cabinet.dll is loaded
@@ -299,15 +299,13 @@ static HRESULT DAPI CabOperation(
299 // If a full path was not provided, use the relative current directory. 299 // If a full path was not provided, use the relative current directory.
300 if (wzCabinet == pwz) 300 if (wzCabinet == pwz)
301 { 301 {
302 hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\"); 302 hr = StrAnsiAllocStringAnsi(&pszCabDirectory, ".\\", 0);
303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory."); 303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
304 } 304 }
305 else 305 else
306 { 306 {
307 if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL)) 307 hr = StrAnsiAllocString(&pszCabDirectory, sczCabinet, 0, CP_UTF8);
308 { 308 CabExitOnFailure(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
309 CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
310 }
311 } 309 }
312 310
313 // 311 //
@@ -331,7 +329,7 @@ static HRESULT DAPI CabOperation(
331 v_pfnNetFx11Notify = pfnNotify; 329 v_pfnNetFx11Notify = pfnNotify;
332 pfnFdiNotify = FDINotify; 330 pfnFdiNotify = FDINotify;
333 } 331 }
334 fResult = vpfnFDICopy(vhfdi, szCabFile, szCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs)); 332 fResult = vpfnFDICopy(vhfdi, szCabFile, pszCabDirectory, 0, pfnFdiNotify, NULL, static_cast<void*>(&ccs));
335 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure 333 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
336 { 334 {
337 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet); 335 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
@@ -339,6 +337,7 @@ static HRESULT DAPI CabOperation(
339 337
340LExit: 338LExit:
341 ReleaseStr(sczCabinet); 339 ReleaseStr(sczCabinet);
340 ReleaseStr(pszCabDirectory);
342 v_pfnNetFx11Notify = NULL; 341 v_pfnNetFx11Notify = NULL;
343 342
344 return hr; 343 return hr;
@@ -493,14 +492,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
493 INT_PTR ipResult = 0; // result to return on success 492 INT_PTR ipResult = 0; // result to return on success
494 493
495 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv); 494 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
496 LPCSTR sz; 495 LPCSTR sz = NULL;
497 WCHAR wz[MAX_PATH]; 496 LPWSTR pwz = NULL;
498 FILETIME ft; 497 LPWSTR pwzPath = NULL;
498 FILETIME ft = { };
499 499
500 switch (iNotification) 500 switch (iNotification)
501 { 501 {
502 case fdintCOPY_FILE: // begin extracting a resource from cabinet 502 case fdintCOPY_FILE: // begin extracting a resource from cabinet
503 CabExitOnNull(pFDINotify->psz1, hr, E_INVALIDARG, "No cabinet file ID given to convert"); 503 Assert(pccs && pFDINotify->psz1);
504 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); 504 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
505 505
506 if (pccs->fStopExtracting) 506 if (pccs->fStopExtracting)
@@ -510,40 +510,37 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
510 510
511 // convert params to useful variables 511 // convert params to useful variables
512 sz = static_cast<LPCSTR>(pFDINotify->psz1); 512 sz = static_cast<LPCSTR>(pFDINotify->psz1);
513 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) 513 CabExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
514 { 514
515 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); 515 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
516 } 516 CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
517 517
518 if (pccs->pfnProgress) 518 if (pccs->pfnProgress)
519 { 519 {
520 hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext); 520 hr = pccs->pfnProgress(TRUE, pwz, pccs->pvContext);
521 if (S_OK != hr) 521 if (S_OK != hr)
522 { 522 {
523 ExitFunction(); 523 ExitFunction();
524 } 524 }
525 } 525 }
526 526
527 if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz)) 527 if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, pwz))
528 { 528 {
529 // get the created date for the resource in the cabinet 529 // get the created date for the resource in the cabinet
530 FILETIME ftLocal; 530 FILETIME ftLocal;
531 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal)) 531 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
532 { 532 {
533 CabExitWithLastError(hr, "failed to get time for resource: %ls", wz); 533 CabExitWithLastError(hr, "failed to get time for resource: %ls", pwz);
534 } 534 }
535 ::LocalFileTimeToFileTime(&ftLocal, &ft); 535 ::LocalFileTimeToFileTime(&ftLocal, &ft);
536 536
537 WCHAR wzPath[MAX_PATH]; 537 hr = PathConcat(pccs->pwzExtractDir, pwz, &pwzPath);
538 hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir); 538 CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", pccs->pwzExtractDir, pwz);
539 CabExitOnFailure(hr, "failed to copy in extract directory: %ls for file: %ls", pccs->pwzExtractDir, wz);
540 hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
541 CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
542 539
543 hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS); 540 hFile = OpenFileWithRetry(pwzPath, GENERIC_WRITE, CREATE_ALWAYS);
544 if (INVALID_HANDLE_VALUE == hFile) 541 if (INVALID_HANDLE_VALUE == hFile)
545 { 542 {
546 CabExitWithLastError(hr, "failed to create file: %ls", wzPath); 543 CabExitWithLastError(hr, "failed to create file: %ls", pwzPath);
547 } 544 }
548 545
549 ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails) 546 ::SetFileTime(hFile, &ft, &ft, &ft); // try to set the file time (who cares if it fails)
@@ -567,17 +564,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
567 564
568 break; 565 break;
569 case fdintCLOSE_FILE_INFO: // resource extraction complete 566 case fdintCLOSE_FILE_INFO: // resource extraction complete
570 Assert(pFDINotify->hf && pFDINotify->psz1); 567 Assert(pFDINotify->hf && pccs && pFDINotify->psz1);
571 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided"); 568 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
572 569
573 // convert params to useful variables 570 // convert params to useful variables
574 sz = static_cast<LPCSTR>(pFDINotify->psz1); 571 sz = static_cast<LPCSTR>(pFDINotify->psz1);
575 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided"); 572 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
576 573
577 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) 574 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
578 { 575 CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
579 CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
580 }
581 576
582 if (NULL != pFDINotify->hf) // just close the file 577 if (NULL != pFDINotify->hf) // just close the file
583 { 578 {
@@ -586,7 +581,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
586 581
587 if (pccs->pfnProgress) 582 if (pccs->pfnProgress)
588 { 583 {
589 hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext); 584 hr = pccs->pfnProgress(FALSE, pwz, pccs->pvContext);
590 } 585 }
591 586
592 if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going 587 if (S_OK == hr && L'*' == *pccs->pwzExtract) // if everything is okay and we're extracting all files, keep going
@@ -613,5 +608,8 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
613LExit: 608LExit:
614 ReleaseFileHandle(hFile); 609 ReleaseFileHandle(hFile);
615 610
611 ReleaseStr(pwz);
612 ReleaseStr(pwzPath);
613
616 return (S_OK == hr) ? ipResult : -1; 614 return (S_OK == hr) ? ipResult : -1;
617} 615}
diff --git a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
index 94eab9e7..2c02225d 100644
--- a/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/dirutil.cpp
@@ -59,34 +59,10 @@ LExit:
59 *******************************************************************/ 59 *******************************************************************/
60extern "C" HRESULT DAPI DirCreateTempPath( 60extern "C" HRESULT DAPI DirCreateTempPath(
61 __in_z LPCWSTR wzPrefix, 61 __in_z LPCWSTR wzPrefix,
62 __out_ecount_z(cchPath) LPWSTR wzPath, 62 __out_opt LPWSTR* psczTempFile
63 __in DWORD cchPath
64 ) 63 )
65{ 64{
66 Assert(wzPrefix); 65 return PathCreateTempFile(NULL, NULL, 0, wzPrefix, 0, psczTempFile, NULL);
67 Assert(wzPath);
68
69 HRESULT hr = S_OK;
70
71 WCHAR wzDir[MAX_PATH];
72 WCHAR wzFile[MAX_PATH];
73 DWORD cch = 0;
74
75 cch = ::GetTempPathW(countof(wzDir), wzDir);
76 if (!cch || cch >= countof(wzDir))
77 {
78 DirExitWithLastError(hr, "Failed to GetTempPath.");
79 }
80
81 if (!::GetTempFileNameW(wzDir, wzPrefix, 0, wzFile))
82 {
83 DirExitWithLastError(hr, "Failed to GetTempFileName.");
84 }
85
86 hr = ::StringCchCopyW(wzPath, cchPath, wzFile);
87
88LExit:
89 return hr;
90} 66}
91 67
92 68
@@ -192,18 +168,19 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
192 Assert(wzPath && *wzPath); 168 Assert(wzPath && *wzPath);
193 169
194 HRESULT hr = S_OK; 170 HRESULT hr = S_OK;
195 DWORD er; 171 DWORD er = ERROR_SUCCESS;
196 172
197 DWORD dwAttrib; 173 DWORD dwAttrib = 0;
198 HANDLE hFind = INVALID_HANDLE_VALUE; 174 HANDLE hFind = INVALID_HANDLE_VALUE;
199 LPWSTR sczDelete = NULL; 175 LPWSTR sczDelete = NULL;
200 WIN32_FIND_DATAW wfd; 176 WIN32_FIND_DATAW wfd = { };
201 177
202 BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES)); 178 BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES));
203 BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE)); 179 BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE));
204 BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE)); 180 BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE));
205 WCHAR wzTempDirectory[MAX_PATH] = { }; 181 WCHAR wzSafeFileName[MAX_PATH + 1] = { };
206 WCHAR wzTempPath[MAX_PATH] = { }; 182 LPWSTR sczTempDirectory = NULL;
183 LPWSTR sczTempPath = NULL;
207 184
208 if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath))) 185 if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath)))
209 { 186 {
@@ -231,10 +208,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
231 { 208 {
232 if (fScheduleDelete) 209 if (fScheduleDelete)
233 { 210 {
234 if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory)) 211 hr = PathGetTempPath(&sczTempDirectory, NULL);
235 { 212 DirExitOnFailure(hr, "Failed to get temp directory.");
236 DirExitWithLastError(hr, "Failed to get temp directory.");
237 }
238 } 213 }
239 214
240 // Delete everything in this directory. 215 // Delete everything in this directory.
@@ -256,10 +231,11 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
256 } 231 }
257 232
258 // For extra safety and to silence OACR. 233 // For extra safety and to silence OACR.
259 wfd.cFileName[MAX_PATH - 1] = L'\0'; 234 hr = ::StringCchCopyNExW(wzSafeFileName, countof(wzSafeFileName), wfd.cFileName, countof(wfd.cFileName), NULL, NULL, STRSAFE_FILL_BEHIND_NULL | STRSAFE_NULL_ON_FAILURE);
235 DirExitOnFailure(hr, "Failed to ensure file name was null terminated.");
260 236
261 hr = PathConcat(wzPath, wfd.cFileName, &sczDelete); 237 hr = PathConcat(wzPath, wzSafeFileName, &sczDelete);
262 DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath); 238 DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wzSafeFileName, wzPath);
263 239
264 if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 240 if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
265 { 241 {
@@ -288,16 +264,14 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
288 { 264 {
289 if (fScheduleDelete) 265 if (fScheduleDelete)
290 { 266 {
291 if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath)) 267 hr = PathGetTempFileName(sczTempDirectory, L"DEL", 0, &sczTempPath);
292 { 268 DirExitOnFailure(hr, "Failed to get temp file to move to.");
293 DirExitWithLastError(hr, "Failed to get temp file to move to.");
294 }
295 269
296 // Try to move the file to the temp directory then schedule for delete, 270 // Try to move the file to the temp directory then schedule for delete,
297 // otherwise just schedule for delete. 271 // otherwise just schedule for delete.
298 if (::MoveFileExW(sczDelete, wzTempPath, MOVEFILE_REPLACE_EXISTING)) 272 if (::MoveFileExW(sczDelete, sczTempPath, MOVEFILE_REPLACE_EXISTING))
299 { 273 {
300 ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); 274 ::MoveFileExW(sczTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
301 } 275 }
302 else 276 else
303 { 277 {
@@ -348,6 +322,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
348LExit: 322LExit:
349 ReleaseFileFindHandle(hFind); 323 ReleaseFileFindHandle(hFind);
350 ReleaseStr(sczDelete); 324 ReleaseStr(sczDelete);
325 ReleaseStr(sczTempDirectory);
326 ReleaseStr(sczTempPath);
351 327
352 return hr; 328 return hr;
353} 329}
diff --git a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
index 9f68ee52..ac407916 100644
--- a/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/fileutil.cpp
@@ -1284,21 +1284,18 @@ extern "C" HRESULT DAPI FileCreateTempW(
1284 Assert(wzPrefix && *wzPrefix); 1284 Assert(wzPrefix && *wzPrefix);
1285 HRESULT hr = E_FAIL; 1285 HRESULT hr = E_FAIL;
1286 1286
1287 WCHAR wzTempPath[MAX_PATH]; 1287 LPWSTR pwzTempPath = NULL;
1288 DWORD cchTempPath = countof(wzTempPath);
1289 LPWSTR pwzTempFile = NULL; 1288 LPWSTR pwzTempFile = NULL;
1290 1289
1291 HANDLE hTempFile = INVALID_HANDLE_VALUE; 1290 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1292 int i = 0; 1291 int i = 0;
1293 1292
1294 if (!::GetTempPathW(cchTempPath, wzTempPath)) 1293 hr = PathGetTempPath(&pwzTempPath, NULL);
1295 { 1294 FileExitOnFailure(hr, "failed to get temp path");
1296 FileExitOnLastError(hr, "failed to get temp path");
1297 }
1298 1295
1299 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i) 1296 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
1300 { 1297 {
1301 hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", wzTempPath, wzPrefix, i, wzExtension); 1298 hr = StrAllocFormatted(&pwzTempFile, L"%s%s%05d.%s", pwzTempPath, wzPrefix, i, wzExtension);
1302 FileExitOnFailure(hr, "failed to allocate memory for temp filename"); 1299 FileExitOnFailure(hr, "failed to allocate memory for temp filename");
1303 1300
1304 hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); 1301 hTempFile = ::CreateFileW(pwzTempFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -1330,6 +1327,7 @@ extern "C" HRESULT DAPI FileCreateTempW(
1330LExit: 1327LExit:
1331 ReleaseFile(hTempFile); 1328 ReleaseFile(hTempFile);
1332 ReleaseStr(pwzTempFile); 1329 ReleaseStr(pwzTempFile);
1330 ReleaseStr(pwzTempPath);
1333 1331
1334 return hr; 1332 return hr;
1335} 1333}
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
index 42268a16..b8fc0431 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
@@ -20,8 +20,7 @@ BOOL DAPI DirExists(
20 20
21HRESULT DAPI DirCreateTempPath( 21HRESULT DAPI DirCreateTempPath(
22 __in_z LPCWSTR wzPrefix, 22 __in_z LPCWSTR wzPrefix,
23 __out_ecount_z(cchPath) LPWSTR wzPath, 23 __out_opt LPWSTR* psczTempFile
24 __in DWORD cchPath
25 ); 24 );
26 25
27HRESULT DAPI DirEnsureExists( 26HRESULT DAPI DirEnsureExists(
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
index 971ef887..de46b95d 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
@@ -159,19 +159,31 @@ DAPI_(HRESULT) PathRelativeToModule(
159/******************************************************************* 159/*******************************************************************
160 PathCreateTempFile 160 PathCreateTempFile
161 161
162 Note: if wzDirectory is null, ::GetTempPath() will be used instead. 162 Note: if wzDirectory is null, ::GetTempPath2() will be used instead.
163 if wzFileNameTemplate is null, GetTempFileName() will be used instead. 163 if wzFileNameTemplate is null, GetTempFileName() will be used instead.
164*******************************************************************/ 164*******************************************************************/
165DAPI_(HRESULT) PathCreateTempFile( 165DAPI_(HRESULT) PathCreateTempFile(
166 __in_opt LPCWSTR wzDirectory, 166 __in_opt LPCWSTR wzDirectory,
167 __in_opt __format_string LPCWSTR wzFileNameTemplate, 167 __in_opt __format_string LPCWSTR wzFileNameTemplate,
168 __in DWORD dwUniqueCount, 168 __in DWORD dwUniqueCount,
169 __in_z LPCWSTR wzPrefix,
169 __in DWORD dwFileAttributes, 170 __in DWORD dwFileAttributes,
170 __out_opt LPWSTR* psczTempFile, 171 __out_opt LPWSTR* psczTempFile,
171 __out_opt HANDLE* phTempFile 172 __out_opt HANDLE* phTempFile
172 ); 173 );
173 174
174/******************************************************************* 175/*******************************************************************
176 PathGetTempFileName - wrapper around ::GetTempFileName.
177 If the wzPathName is too long, it will use its own algorithm.
178*******************************************************************/
179DAPI_(HRESULT) PathGetTempFileName(
180 __in LPCWSTR wzPathName,
181 __in LPCWSTR wzPrefixString,
182 __in UINT uUnique,
183 __out LPWSTR* psczTempFileName
184 );
185
186/*******************************************************************
175 PathCreateTimeBasedTempFile - creates an empty temp file based on current 187 PathCreateTimeBasedTempFile - creates an empty temp file based on current
176 system time 188 system time
177********************************************************************/ 189********************************************************************/
@@ -187,7 +199,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
187/******************************************************************* 199/*******************************************************************
188 PathCreateTempDirectory 200 PathCreateTempDirectory
189 201
190 Note: if wzDirectory is null, ::GetTempPath() will be used instead. 202 Note: if wzDirectory is null, ::GetTempPath2() will be used instead.
191*******************************************************************/ 203*******************************************************************/
192DAPI_(HRESULT) PathCreateTempDirectory( 204DAPI_(HRESULT) PathCreateTempDirectory(
193 __in_opt LPCWSTR wzDirectory, 205 __in_opt LPCWSTR wzDirectory,
@@ -201,7 +213,24 @@ DAPI_(HRESULT) PathCreateTempDirectory(
201 that is backslash terminated. 213 that is backslash terminated.
202*******************************************************************/ 214*******************************************************************/
203DAPI_(HRESULT) PathGetTempPath( 215DAPI_(HRESULT) PathGetTempPath(
204 __out_z LPWSTR* psczTempPath 216 __out_z LPWSTR* psczTempPath,
217 __out_opt SIZE_T* pcch
218 );
219
220/*******************************************************************
221 PathGetSystemDirectory - returns the path to the system folder
222 that is backslash terminated.
223*******************************************************************/
224DAPI_(HRESULT) PathGetSystemDirectory(
225 __out_z LPWSTR* psczSystemPath
226 );
227
228/*******************************************************************
229 PathGetSystemWow64Directory - returns the path to the system WoW 64 folder
230 that is backslash terminated.
231*******************************************************************/
232DAPI_(HRESULT) PathGetSystemWow64Directory(
233 __out_z LPWSTR* psczSystemPath
205 ); 234 );
206 235
207/******************************************************************* 236/*******************************************************************
@@ -223,12 +252,11 @@ DAPI_(HRESULT) PathGetSystemTempPaths(
223 ); 252 );
224 253
225/******************************************************************* 254/*******************************************************************
226 PathGetKnownFolder - returns the path to a well-known shell folder 255 PathGetVolumePathName - wrapper for ::GetVolumePathNameW.
227
228*******************************************************************/ 256*******************************************************************/
229DAPI_(HRESULT) PathGetKnownFolder( 257DAPI_(HRESULT) PathGetVolumePathName(
230 __in int csidl, 258 __in_z LPCWSTR wzFileName,
231 __out LPWSTR* psczKnownFolder 259 __out_z LPWSTR* psczVolumePathName
232 ); 260 );
233 261
234/******************************************************************* 262/*******************************************************************
@@ -340,6 +368,20 @@ DAPI_(HRESULT) PathGetHierarchyArray(
340 __inout LPUINT pcPathArray 368 __inout LPUINT pcPathArray
341 ); 369 );
342 370
371/********************************************************************
372 Path2FunctionAllowFallback - allow functions only available in newer versions of Windows.
373 Typically used for unit testing.
374
375*********************************************************************/
376void DAPI Path2FunctionAllowFallback();
377
378/********************************************************************
379 Path2FunctionForceFallback - ignore functions only available in newer versions of Windows.
380 Typically used for unit testing.
381
382*********************************************************************/
383void DAPI Path2FunctionForceFallback();
384
343/******************************************************************* 385/*******************************************************************
344 PathCanonicalizePath - wrapper around PathCanonicalizeW. 386 PathCanonicalizePath - wrapper around PathCanonicalizeW.
345*******************************************************************/ 387*******************************************************************/
@@ -349,6 +391,15 @@ DAPI_(HRESULT) PathCanonicalizePath(
349 ); 391 );
350 392
351/******************************************************************* 393/*******************************************************************
394 PathAllocCanonicalizePath - wrapper around PathAllocCanonicalize.
395*******************************************************************/
396DAPI_(HRESULT) PathAllocCanonicalizePath(
397 __in_z LPCWSTR wzPath,
398 __in DWORD dwFlags,
399 __deref_out_z LPWSTR* psczCanonicalized
400 );
401
402/*******************************************************************
352 PathCanonicalizeForComparison - canonicalizes the path based on the given flags. 403 PathCanonicalizeForComparison - canonicalizes the path based on the given flags.
353 . and .. directories are collapsed. 404 . and .. directories are collapsed.
354 All / are replaced with \. 405 All / are replaced with \.
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
index 0b9f539d..2ee7ce87 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
@@ -33,10 +33,25 @@ HRESULT DAPI ShelExecUnelevated(
33 __in_z_opt LPCWSTR wzWorkingDirectory, 33 __in_z_opt LPCWSTR wzWorkingDirectory,
34 __in int nShowCmd 34 __in int nShowCmd
35 ); 35 );
36
37/********************************************************************
38 ShelGetFolder() - translates the CSIDL into KNOWNFOLDERID and calls ShelGetKnownFolder.
39 If that returns E_NOTIMPL then falls back to ::SHGetFolderPathW.
40 The CSIDL_FLAG values are not supported, CSIDL_FLAG_CREATE is always used.
41 The path is backslash terminated.
42
43*******************************************************************/
36HRESULT DAPI ShelGetFolder( 44HRESULT DAPI ShelGetFolder(
37 __out_z LPWSTR* psczFolderPath, 45 __out_z LPWSTR* psczFolderPath,
38 __in int csidlFolder 46 __in int csidlFolder
39 ); 47 );
48
49/********************************************************************
50 ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID with ::SHGetKnownFolderPath.
51 The path is backslash terminated.
52
53 Note: return E_NOTIMPL if called on pre-Vista operating systems.
54*******************************************************************/
40HRESULT DAPI ShelGetKnownFolder( 55HRESULT DAPI ShelGetKnownFolder(
41 __out_z LPWSTR* psczFolderPath, 56 __out_z LPWSTR* psczFolderPath,
42 __in REFKNOWNFOLDERID rfidFolder 57 __in REFKNOWNFOLDERID rfidFolder
diff --git a/src/libs/dutil/WixToolset.DUtil/logutil.cpp b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
index 88a90d8c..3a130b4e 100644
--- a/src/libs/dutil/WixToolset.DUtil/logutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/logutil.cpp
@@ -684,9 +684,10 @@ LExit:
684extern "C" HRESULT DAPI LogHeader() 684extern "C" HRESULT DAPI LogHeader()
685{ 685{
686 HRESULT hr = S_OK; 686 HRESULT hr = S_OK;
687 WCHAR wzComputerName[MAX_PATH]; 687 WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { };
688 DWORD cchComputerName = countof(wzComputerName); 688 DWORD cchComputerName = countof(wzComputerName);
689 WCHAR wzPath[MAX_PATH]; 689 LPWSTR sczPath = NULL;
690 LPCWSTR wzPath = NULL;
690 DWORD dwMajorVersion = 0; 691 DWORD dwMajorVersion = 0;
691 DWORD dwMinorVersion = 0; 692 DWORD dwMinorVersion = 0;
692 LPCSTR szLevel = LOGUTIL_UNKNOWN; 693 LPCSTR szLevel = LOGUTIL_UNKNOWN;
@@ -695,12 +696,19 @@ extern "C" HRESULT DAPI LogHeader()
695 // 696 //
696 // get the interesting data 697 // get the interesting data
697 // 698 //
698 if (!::GetModuleFileNameW(NULL, wzPath, countof(wzPath))) 699
700 hr = PathForCurrentProcess(&sczPath, NULL);
701 if (FAILED(hr))
702 {
703 wzPath = L"";
704 }
705 else
699 { 706 {
700 memset(wzPath, 0, sizeof(wzPath)); 707 wzPath = sczPath;
708
709 hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
701 } 710 }
702 711
703 hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
704 if (FAILED(hr)) 712 if (FAILED(hr))
705 { 713 {
706 dwMajorVersion = 0; 714 dwMajorVersion = 0;
@@ -743,6 +751,7 @@ extern "C" HRESULT DAPI LogHeader()
743 hr = S_OK; 751 hr = S_OK;
744 752
745 ReleaseStr(sczCurrentDateTime); 753 ReleaseStr(sczCurrentDateTime);
754 ReleaseStr(sczPath);
746 755
747 return hr; 756 return hr;
748} 757}
diff --git a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
index 3c4b2f88..862a743d 100644
--- a/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/path2utl.cpp
@@ -19,6 +19,65 @@
19#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__) 19#define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__)
20 20
21 21
22typedef HRESULT(WINAPI* PFN_PATH_ALLOC_CANONICALIZE)(
23 __in LPCWSTR wzSource,
24 __in DWORD dwFlags,
25 __out_z LPWSTR* psczPathOut
26 );
27
28static BOOL vfInitialized = FALSE;
29static HMODULE vhPathApiSet_1_1_0 = NULL;
30static PFN_PATH_ALLOC_CANONICALIZE vpfnPathAllocCanonicalize = NULL;
31static BOOL vfForceFallback = FALSE;
32
33// from PathCch.h
34#ifndef PATHCCH_ALLOW_LONG_PATHS
35#define PATHCCH_ALLOW_LONG_PATHS 0x01
36#endif
37
38static HRESULT Initialize()
39{
40 HRESULT hr = S_OK;
41 DWORD er = ERROR_SUCCESS;
42
43 if (vfInitialized)
44 {
45 ExitFunction();
46 }
47
48 hr = LoadSystemApiSet(L"api-ms-win-core-path-l1-1-0.dll", &vhPathApiSet_1_1_0);
49 if (E_MODNOTFOUND == hr)
50 {
51 hr = E_NOTIMPL;
52 }
53 PathExitOnFailure(hr, "Failed to load api-ms-win-core-path-l1-1-0.dll");
54
55 vpfnPathAllocCanonicalize = reinterpret_cast<PFN_PATH_ALLOC_CANONICALIZE>(::GetProcAddress(vhPathApiSet_1_1_0, "PathAllocCanonicalize"));
56 if (!vpfnPathAllocCanonicalize)
57 {
58 er = ::GetLastError();
59 PathExitWithRootFailure(hr, ERROR_PROC_NOT_FOUND == er ? E_NOTIMPL : HRESULT_FROM_WIN32(er), "Failed to get address of PathAllocCanonicalize.");
60 }
61
62 vfInitialized = TRUE;
63
64LExit:
65 return hr;
66}
67
68
69DAPI_(void) Path2FunctionAllowFallback()
70{
71 vfForceFallback = FALSE;
72}
73
74
75DAPI_(void) Path2FunctionForceFallback()
76{
77 vfForceFallback = TRUE;
78}
79
80
22DAPI_(HRESULT) PathCanonicalizePath( 81DAPI_(HRESULT) PathCanonicalizePath(
23 __in_z LPCWSTR wzPath, 82 __in_z LPCWSTR wzPath,
24 __deref_out_z LPWSTR* psczCanonicalized 83 __deref_out_z LPWSTR* psczCanonicalized
@@ -43,6 +102,37 @@ LExit:
43 return hr; 102 return hr;
44} 103}
45 104
105DAPI_(HRESULT) PathAllocCanonicalizePath(
106 __in_z LPCWSTR wzPath,
107 __in DWORD dwFlags,
108 __deref_out_z LPWSTR* psczCanonicalized
109 )
110{
111 HRESULT hr = S_OK;
112 LPWSTR sczCanonicalizedPath = NULL;
113
114 hr = Initialize();
115 if (E_NOTIMPL == hr || SUCCEEDED(hr) && !vpfnPathAllocCanonicalize)
116 {
117 ExitFunction1(hr = E_NOTIMPL);
118 }
119 PathExitOnFailure(hr, "Failed to initialize path2utl.");
120
121 hr = vpfnPathAllocCanonicalize(wzPath, dwFlags, &sczCanonicalizedPath);
122 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzPath);
123
124 hr = StrAllocString(psczCanonicalized, sczCanonicalizedPath, 0);
125 PathExitOnFailure(hr, "Failed to copy the canonicalized path.");
126
127LExit:
128 if (sczCanonicalizedPath)
129 {
130 ::LocalFree(sczCanonicalizedPath);
131 }
132
133 return hr;
134}
135
46DAPI_(HRESULT) PathCanonicalizeForComparison( 136DAPI_(HRESULT) PathCanonicalizeForComparison(
47 __in_z LPCWSTR wzPath, 137 __in_z LPCWSTR wzPath,
48 __in DWORD dwCanonicalizeFlags, 138 __in DWORD dwCanonicalizeFlags,
@@ -75,7 +165,19 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
75 165
76 if (*wzNormalizedPath) 166 if (*wzNormalizedPath)
77 { 167 {
78 hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized); 168 if (!vfForceFallback)
169 {
170 hr = PathAllocCanonicalizePath(wzNormalizedPath, PATHCCH_ALLOW_LONG_PATHS, psczCanonicalized);
171 }
172 else
173 {
174 hr = E_NOTIMPL;
175 }
176
177 if (E_NOTIMPL == hr)
178 {
179 hr = PathCanonicalizePath(wzNormalizedPath, psczCanonicalized);
180 }
79 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath); 181 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath);
80 } 182 }
81 else 183 else
@@ -273,33 +375,52 @@ DAPI_(HRESULT) PathSystemWindowsSubdirectory(
273 ) 375 )
274{ 376{
275 HRESULT hr = S_OK; 377 HRESULT hr = S_OK;
276 WCHAR wzTempPath[MAX_PATH + 1] = { }; 378 LPWSTR sczWindowsPath = NULL;
379 DWORD cchBuffer = MAX_PATH + 1;
277 DWORD cch = 0; 380 DWORD cch = 0;
278 381
279 cch = ::GetSystemWindowsDirectoryW(wzTempPath, countof(wzTempPath)); 382 hr = StrAlloc(&sczWindowsPath, cchBuffer);
280 if (!cch) 383 PathExitOnFailure(hr, "Failed to alloc Windows directory path.");
281 { 384
282 PathExitWithLastError(hr, "Failed to get Windows directory path."); 385 cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer);
283 } 386 PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with default size.");
284 else if (cch >= countof(wzTempPath)) 387
388 cch += 1; // add 1 for null terminator.
389
390 if (cch > cchBuffer)
285 { 391 {
286 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Windows directory path too long."); 392 hr = StrAlloc(&sczWindowsPath, cch);
393 PathExitOnFailure(hr, "Failed to realloc Windows directory path.");
394
395 cchBuffer = cch;
396
397 cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer);
398 PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with returned size.");
399
400 cch += 1; // add 1 for null terminator.
401
402 if (cch > cchBuffer)
403 {
404 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get Windows directory path with returned size.");
405 }
287 } 406 }
288 407
289 if (wzSubdirectory) 408 if (wzSubdirectory)
290 { 409 {
291 hr = PathConcatRelativeToBase(wzTempPath, wzSubdirectory, psczFullPath); 410 hr = PathConcatRelativeToBase(sczWindowsPath, wzSubdirectory, psczFullPath);
292 PathExitOnFailure(hr, "Failed to concat subdirectory on Windows directory path."); 411 PathExitOnFailure(hr, "Failed to concat subdirectory on Windows directory path.");
293 } 412 }
294 else 413 else
295 { 414 {
296 hr = StrAllocString(psczFullPath, wzTempPath, 0); 415 *psczFullPath = sczWindowsPath;
297 PathExitOnFailure(hr, "Failed to copy Windows directory path."); 416 sczWindowsPath = NULL;
298 } 417 }
299 418
300 hr = PathBackslashTerminate(psczFullPath); 419 hr = PathBackslashTerminate(psczFullPath);
301 PathExitOnFailure(hr, "Failed to terminate Windows directory path with backslash."); 420 PathExitOnFailure(hr, "Failed to terminate Windows directory path with backslash.");
302 421
303LExit: 422LExit:
423 ReleaseStr(sczWindowsPath);
424
304 return hr; 425 return hr;
305} 426}
diff --git a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
index 0e2a5dec..dd5385fc 100644
--- a/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/pathutil.cpp
@@ -20,6 +20,11 @@
20 20
21#define PATH_GOOD_ENOUGH 64 21#define PATH_GOOD_ENOUGH 64
22 22
23typedef DWORD(APIENTRY* PFN_GETTEMPPATH2W)(
24 __in DWORD BufferLength,
25 __out LPWSTR Buffer
26 );
27
23static BOOL IsPathSeparatorChar( 28static BOOL IsPathSeparatorChar(
24 __in WCHAR wc 29 __in WCHAR wc
25 ); 30 );
@@ -527,28 +532,55 @@ DAPI_(HRESULT) PathForCurrentProcess(
527 ) 532 )
528{ 533{
529 HRESULT hr = S_OK; 534 HRESULT hr = S_OK;
530 DWORD cch = MAX_PATH; 535 WCHAR smallBuffer[1] = { };
536 SIZE_T cchMax = 0;
537 DWORD cchBuffer = 0;
538 DWORD cch = 0;
539 DWORD dwMaxAttempts = 20;
531 540
532 do 541 // GetModuleFileNameW didn't originally set the last error when the buffer was too small.
542 ::SetLastError(ERROR_SUCCESS);
543
544 cch = ::GetModuleFileNameW(hModule, smallBuffer, countof(smallBuffer));
545 PathExitOnNullWithLastError(cch, hr, "Failed to get size of path for executing process.");
546
547 if (*psczFullPath && ERROR_INSUFFICIENT_BUFFER == ::GetLastError())
533 { 548 {
534 hr = StrAlloc(psczFullPath, cch); 549 hr = StrMaxLength(*psczFullPath, &cchMax);
535 PathExitOnFailure(hr, "Failed to allocate string for module path."); 550 PathExitOnFailure(hr, "Failed to get max length of input buffer.");
551
552 cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
553 }
554 else
555 {
556 cchBuffer = MAX_PATH + 1;
557
558 hr = StrAlloc(psczFullPath, cchBuffer);
559 PathExitOnFailure(hr, "Failed to allocate space for module path.");
560 }
561
562 ::SetLastError(ERROR_SUCCESS);
536 563
537 DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch); 564 for (DWORD i = 0; i < dwMaxAttempts; ++i)
538 if (0 == cchRequired) 565 {
566 cch = ::GetModuleFileNameW(hModule, *psczFullPath, cchBuffer);
567 PathExitOnNullWithLastError(cch, hr, "Failed to get path for executing process.");
568
569 if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
539 { 570 {
540 PathExitWithLastError(hr, "Failed to get path for executing process."); 571 break;
541 } 572 }
542 else if (cchRequired == cch) 573
574 if ((dwMaxAttempts - 1) == i)
543 { 575 {
544 cch = cchRequired + 1; 576 PathExitWithRootFailure(hr, E_FAIL, "Unexpected failure getting path for executing process.");
545 hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
546 } 577 }
547 else 578
548 { 579 cchBuffer *= 2;
549 hr = S_OK; 580
550 } 581 hr = StrAlloc(psczFullPath, cchBuffer);
551 } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr); 582 PathExitOnFailure(hr, "Failed to re-allocate more space for module path.");
583 }
552 584
553LExit: 585LExit:
554 return hr; 586 return hr;
@@ -582,17 +614,18 @@ DAPI_(HRESULT) PathCreateTempFile(
582 __in_opt LPCWSTR wzDirectory, 614 __in_opt LPCWSTR wzDirectory,
583 __in_opt __format_string LPCWSTR wzFileNameTemplate, 615 __in_opt __format_string LPCWSTR wzFileNameTemplate,
584 __in DWORD dwUniqueCount, 616 __in DWORD dwUniqueCount,
617 __in_z LPCWSTR wzPrefix,
585 __in DWORD dwFileAttributes, 618 __in DWORD dwFileAttributes,
586 __out_opt LPWSTR* psczTempFile, 619 __out_opt LPWSTR* psczTempFile,
587 __out_opt HANDLE* phTempFile 620 __out_opt HANDLE* phTempFile
588 ) 621 )
589{ 622{
590 AssertSz(0 < dwUniqueCount, "Must specify a non-zero unique count."); 623 Assert(wzPrefix);
624 AssertSz(!wzFileNameTemplate || !*wzFileNameTemplate || 0 < dwUniqueCount, "Must specify a non-zero unique count.");
591 625
592 HRESULT hr = S_OK; 626 HRESULT hr = S_OK;
593 627
594 LPWSTR sczTempPath = NULL; 628 LPWSTR sczTempPath = NULL;
595 DWORD cchTempPath = MAX_PATH;
596 629
597 HANDLE hTempFile = INVALID_HANDLE_VALUE; 630 HANDLE hTempFile = INVALID_HANDLE_VALUE;
598 LPWSTR scz = NULL; 631 LPWSTR scz = NULL;
@@ -605,13 +638,8 @@ DAPI_(HRESULT) PathCreateTempFile(
605 } 638 }
606 else 639 else
607 { 640 {
608 hr = StrAlloc(&sczTempPath, cchTempPath); 641 hr = PathGetTempPath(&sczTempPath, NULL);
609 PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); 642 PathExitOnFailure(hr, "Failed to get temp path.");
610
611 if (!::GetTempPathW(cchTempPath, sczTempPath))
612 {
613 PathExitWithLastError(hr, "Failed to get temp path.");
614 }
615 } 643 }
616 644
617 if (wzFileNameTemplate && *wzFileNameTemplate) 645 if (wzFileNameTemplate && *wzFileNameTemplate)
@@ -621,7 +649,7 @@ DAPI_(HRESULT) PathCreateTempFile(
621 hr = StrAllocFormatted(&scz, wzFileNameTemplate, i); 649 hr = StrAllocFormatted(&scz, wzFileNameTemplate, i);
622 PathExitOnFailure(hr, "Failed to allocate memory for file template."); 650 PathExitOnFailure(hr, "Failed to allocate memory for file template.");
623 651
624 hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz); 652 hr = StrAllocFormatted(&sczTempFile, L"%ls%ls", sczTempPath, scz);
625 PathExitOnFailure(hr, "Failed to allocate temp file name."); 653 PathExitOnFailure(hr, "Failed to allocate temp file name.");
626 654
627 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL); 655 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL);
@@ -642,13 +670,8 @@ DAPI_(HRESULT) PathCreateTempFile(
642 // the system to provide us a temp file using its built-in mechanism. 670 // the system to provide us a temp file using its built-in mechanism.
643 if (INVALID_HANDLE_VALUE == hTempFile) 671 if (INVALID_HANDLE_VALUE == hTempFile)
644 { 672 {
645 hr = StrAlloc(&sczTempFile, MAX_PATH); 673 hr = PathGetTempFileName(sczTempPath, wzPrefix, 0, &sczTempFile);
646 PathExitOnFailure(hr, "Failed to allocate memory for the temp path"); 674 PathExitOnFailure(hr, "Failed to create new temp file name.");
647
648 if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile))
649 {
650 PathExitWithLastError(hr, "Failed to create new temp file name.");
651 }
652 675
653 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL); 676 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL);
654 if (INVALID_HANDLE_VALUE == hTempFile) 677 if (INVALID_HANDLE_VALUE == hTempFile)
@@ -684,6 +707,82 @@ LExit:
684} 707}
685 708
686 709
710DAPI_(HRESULT) PathGetTempFileName(
711 __in LPCWSTR wzPathName,
712 __in LPCWSTR wzPrefixString,
713 __in UINT uUnique,
714 __out LPWSTR* psczTempFileName
715 )
716{
717 HRESULT hr = S_OK;
718 size_t cchFullPath = 0;
719 WORD wValue = (WORD)(0xffff & uUnique);
720 LPWSTR scz = NULL;
721 LPWSTR sczTempFile = NULL;
722 HANDLE hTempFile = INVALID_HANDLE_VALUE;
723
724 hr = ::StringCchLengthW(wzPathName, STRSAFE_MAX_CCH, &cchFullPath);
725 PathExitOnFailure(hr, "Failed to get length of path to prefix.");
726
727 if (MAX_PATH - 14 >= cchFullPath)
728 {
729 hr = StrAlloc(psczTempFileName, MAX_PATH);
730 PathExitOnFailure(hr, "Failed to allocate buffer for GetTempFileNameW.");
731
732 if (!::GetTempFileNameW(wzPathName, wzPrefixString, uUnique, *psczTempFileName))
733 {
734 PathExitWithLastError(hr, "Failed to create new temp file name.");
735 }
736
737 ExitFunction();
738 }
739
740 // TODO: when uUnique is 0, consider not always starting at 0 to avoid collisions if this is called repeatedly.
741 // Purposely let it wrap around.
742 for (WORD w = 0; w < WORD_MAX && INVALID_HANDLE_VALUE == hTempFile; ++wValue)
743 {
744 hr = StrAllocFormatted(&scz, L"%ls%x.TMP", wzPrefixString, w);
745 PathExitOnFailure(hr, "Failed to allocate memory for file template.");
746
747 hr = PathConcat(wzPathName, scz, &sczTempFile);
748 PathExitOnFailure(hr, "Failed to allocate temp file name.");
749
750 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, 0, NULL);
751 if (INVALID_HANDLE_VALUE == hTempFile)
752 {
753 // if the file already exists, try next one.
754 hr = HRESULT_FROM_WIN32(::GetLastError());
755 if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
756 {
757 hr = S_OK;
758 }
759 PathExitOnFailure(hr, "Failed to create file: %ls", sczTempFile);
760 }
761
762 ++w;
763 }
764
765 if (INVALID_HANDLE_VALUE == hTempFile)
766 {
767 PathExitWithRootFailure(hr, HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "Failed to create temp file.");
768 }
769
770 hr = StrAllocString(psczTempFileName, sczTempFile, 0);
771 PathExitOnFailure(hr, "Failed to copy temp file string.");
772
773LExit:
774 if (INVALID_HANDLE_VALUE != hTempFile)
775 {
776 ::CloseHandle(hTempFile);
777 }
778
779 ReleaseStr(scz);
780 ReleaseStr(sczTempFile);
781
782 return hr;
783}
784
785
687DAPI_(HRESULT) PathCreateTimeBasedTempFile( 786DAPI_(HRESULT) PathCreateTimeBasedTempFile(
688 __in_z_opt LPCWSTR wzDirectory, 787 __in_z_opt LPCWSTR wzDirectory,
689 __in_z LPCWSTR wzPrefix, 788 __in_z LPCWSTR wzPrefix,
@@ -695,7 +794,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
695{ 794{
696 HRESULT hr = S_OK; 795 HRESULT hr = S_OK;
697 BOOL fRetry = FALSE; 796 BOOL fRetry = FALSE;
698 WCHAR wzTempPath[MAX_PATH] = { }; 797 LPWSTR sczTempParentPath = NULL;
699 LPWSTR sczPrefix = NULL; 798 LPWSTR sczPrefix = NULL;
700 LPWSTR sczPrefixFolder = NULL; 799 LPWSTR sczPrefixFolder = NULL;
701 SYSTEMTIME time = { }; 800 SYSTEMTIME time = { };
@@ -711,12 +810,10 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
711 } 810 }
712 else 811 else
713 { 812 {
714 if (!::GetTempPathW(countof(wzTempPath), wzTempPath)) 813 hr = PathGetTempPath(&sczTempParentPath, NULL);
715 { 814 PathExitOnFailure(hr, "Failed to get temp folder.");
716 PathExitWithLastError(hr, "Failed to get temp folder.");
717 }
718 815
719 hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix); 816 hr = PathConcat(sczTempParentPath, wzPrefix, &sczPrefix);
720 PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix."); 817 PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix.");
721 } 818 }
722 819
@@ -778,6 +875,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
778 875
779LExit: 876LExit:
780 ReleaseFile(hTempFile); 877 ReleaseFile(hTempFile);
878 ReleaseStr(sczTempParentPath);
781 ReleaseStr(sczTempPath); 879 ReleaseStr(sczTempPath);
782 ReleaseStr(sczPrefixFolder); 880 ReleaseStr(sczPrefixFolder);
783 ReleaseStr(sczPrefix); 881 ReleaseStr(sczPrefix);
@@ -799,7 +897,6 @@ DAPI_(HRESULT) PathCreateTempDirectory(
799 HRESULT hr = S_OK; 897 HRESULT hr = S_OK;
800 898
801 LPWSTR sczTempPath = NULL; 899 LPWSTR sczTempPath = NULL;
802 DWORD cchTempPath = MAX_PATH;
803 900
804 LPWSTR scz = NULL; 901 LPWSTR scz = NULL;
805 902
@@ -813,13 +910,8 @@ DAPI_(HRESULT) PathCreateTempDirectory(
813 } 910 }
814 else 911 else
815 { 912 {
816 hr = StrAlloc(&sczTempPath, cchTempPath); 913 hr = PathGetTempPath(&sczTempPath, NULL);
817 PathExitOnFailure(hr, "Failed to allocate memory for the temp path."); 914 PathExitOnFailure(hr, "Failed to get temp path.");
818
819 if (!::GetTempPathW(cchTempPath, sczTempPath))
820 {
821 PathExitWithLastError(hr, "Failed to get temp path.");
822 }
823 } 915 }
824 916
825 for (DWORD i = 1; i <= dwUniqueCount; ++i) 917 for (DWORD i = 1; i <= dwUniqueCount; ++i)
@@ -869,46 +961,230 @@ LExit:
869 961
870 962
871DAPI_(HRESULT) PathGetTempPath( 963DAPI_(HRESULT) PathGetTempPath(
872 __out_z LPWSTR* psczTempPath 964 __out_z LPWSTR* psczTempPath,
965 __out_opt SIZE_T* pcch
966 )
967{
968
969 HRESULT hr = S_OK;
970 SIZE_T cchMax = 0;
971 DWORD cchBuffer = 0;
972 DWORD cch = 0;
973 DWORD dwAttempts = 0;
974 HMODULE hModule = NULL;
975 PFN_GETTEMPPATH2W pfnGetTempPath = NULL;
976 const DWORD dwMaxAttempts = 10;
977
978 if (*psczTempPath)
979 {
980 hr = StrMaxLength(*psczTempPath, &cchMax);
981 PathExitOnFailure(hr, "Failed to get max length of input buffer.");
982
983 cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
984 }
985 else
986 {
987 cchBuffer = MAX_PATH + 1;
988
989 hr = StrAlloc(psczTempPath, cchBuffer);
990 PathExitOnFailure(hr, "Failed to allocate space for temp path.");
991 }
992
993 hr = LoadSystemLibrary(L"kernel32.dll", &hModule);
994 PathExitOnFailure(hr, "Failed to load kernel32.dll");
995
996 pfnGetTempPath = reinterpret_cast<PFN_GETTEMPPATH2W>(::GetProcAddress(hModule, "GetTempPath2W"));
997 if (!pfnGetTempPath)
998 {
999 pfnGetTempPath = ::GetTempPathW;
1000 }
1001
1002 for (; dwAttempts < dwMaxAttempts; ++dwAttempts)
1003 {
1004 cch = pfnGetTempPath(cchBuffer, *psczTempPath);
1005 PathExitOnNullWithLastError(cch, hr, "Failed to get temp path.");
1006
1007 cch += 1; // add one for null terminator.
1008
1009 if (cch <= cchBuffer)
1010 {
1011 break;
1012 }
1013
1014 hr = StrAlloc(psczTempPath, cch);
1015 PathExitOnFailure(hr, "Failed to reallocate space for temp path.");
1016
1017 cchBuffer = cch;
1018 }
1019
1020 if (dwMaxAttempts == dwAttempts)
1021 {
1022 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "GetTempPathW results never converged.");
1023 }
1024
1025 if (pcch)
1026 {
1027 *pcch = cch - 1; // remove one for null terminator.
1028 }
1029
1030LExit:
1031 return hr;
1032}
1033
1034
1035DAPI_(HRESULT) PathGetSystemDirectory(
1036 __out_z LPWSTR* psczSystemPath
1037 )
1038{
1039 HRESULT hr = S_OK;
1040 SIZE_T cchMax = 0;
1041 DWORD cchBuffer = 0;
1042 DWORD cch = 0;
1043
1044 if (*psczSystemPath)
1045 {
1046 hr = StrMaxLength(*psczSystemPath, &cchMax);
1047 PathExitOnFailure(hr, "Failed to get max length of input buffer.");
1048
1049 cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
1050 }
1051 else
1052 {
1053 cchBuffer = MAX_PATH + 1;
1054
1055 hr = StrAlloc(psczSystemPath, cchBuffer);
1056 PathExitOnFailure(hr, "Failed to allocate space for system directory.");
1057 }
1058
1059 cch = ::GetSystemDirectoryW(*psczSystemPath, cchBuffer);
1060 PathExitOnNullWithLastError(cch, hr, "Failed to get system directory path with default size.");
1061
1062 if (cch > cchBuffer)
1063 {
1064 hr = StrAlloc(psczSystemPath, cch);
1065 PathExitOnFailure(hr, "Failed to realloc system directory path.");
1066
1067 cchBuffer = cch;
1068
1069 cch = ::GetSystemDirectoryW(*psczSystemPath, cchBuffer);
1070 PathExitOnNullWithLastError(cch, hr, "Failed to get system directory path with returned size.");
1071
1072 if (cch > cchBuffer)
1073 {
1074 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get system directory path with returned size.");
1075 }
1076 }
1077
1078 hr = PathBackslashTerminate(psczSystemPath);
1079 PathExitOnFailure(hr, "Failed to terminate system directory path with backslash.");
1080
1081LExit:
1082 return hr;
1083}
1084
1085
1086DAPI_(HRESULT) PathGetSystemWow64Directory(
1087 __out_z LPWSTR* psczSystemPath
873 ) 1088 )
874{ 1089{
875 HRESULT hr = S_OK; 1090 HRESULT hr = S_OK;
876 WCHAR wzTempPath[MAX_PATH + 1] = { }; 1091 SIZE_T cchMax = 0;
1092 DWORD cchBuffer = 0;
877 DWORD cch = 0; 1093 DWORD cch = 0;
878 1094
879 cch = ::GetTempPathW(countof(wzTempPath), wzTempPath); 1095 if (*psczSystemPath)
880 if (!cch)
881 { 1096 {
882 PathExitWithLastError(hr, "Failed to GetTempPath."); 1097 hr = StrMaxLength(*psczSystemPath, &cchMax);
1098 PathExitOnFailure(hr, "Failed to get max length of input buffer.");
1099
1100 cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
883 } 1101 }
884 else if (cch >= countof(wzTempPath)) 1102 else
885 { 1103 {
886 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "TEMP directory path too long."); 1104 cchBuffer = MAX_PATH + 1;
1105
1106 hr = StrAlloc(psczSystemPath, cchBuffer);
1107 PathExitOnFailure(hr, "Failed to allocate space for system wow64 directory.");
887 } 1108 }
888 1109
889 hr = StrAllocString(psczTempPath, wzTempPath, cch); 1110 cch = ::GetSystemWow64DirectoryW(*psczSystemPath, cchBuffer);
890 PathExitOnFailure(hr, "Failed to copy TEMP directory path."); 1111 PathExitOnNullWithLastError(cch, hr, "Failed to get system wow64 directory path with default size.");
1112
1113 cch += 1; // add one for the null terminator.
1114
1115 if (cch > cchBuffer)
1116 {
1117 hr = StrAlloc(psczSystemPath, cch);
1118 PathExitOnFailure(hr, "Failed to realloc system wow64 directory path.");
1119
1120 cchBuffer = cch;
1121
1122 cch = ::GetSystemWow64DirectoryW(*psczSystemPath, cchBuffer);
1123 PathExitOnNullWithLastError(cch, hr, "Failed to get system wow64 directory path with returned size.");
1124
1125 cch += 1; // add one for the null terminator.
1126
1127 if (cch > cchBuffer)
1128 {
1129 PathExitWithRootFailure(hr, E_INSUFFICIENT_BUFFER, "Failed to get system wow64 directory path with returned size.");
1130 }
1131 }
1132
1133 hr = PathBackslashTerminate(psczSystemPath);
1134 PathExitOnFailure(hr, "Failed to terminate system wow64 directory path with backslash.");
891 1135
892LExit: 1136LExit:
893 return hr; 1137 return hr;
894} 1138}
895 1139
896 1140
897DAPI_(HRESULT) PathGetKnownFolder( 1141DAPI_(HRESULT) PathGetVolumePathName(
898 __in int csidl, 1142 __in_z LPCWSTR wzFileName,
899 __out LPWSTR* psczKnownFolder 1143 __out_z LPWSTR* psczVolumePathName
900 ) 1144 )
901{ 1145{
902 HRESULT hr = S_OK; 1146 HRESULT hr = S_OK;
1147 DWORD cchBuffer = 0;
1148 SIZE_T cchMax = 0;
1149 const DWORD dwMaxAttempts = 20;
903 1150
904 hr = StrAlloc(psczKnownFolder, MAX_PATH); 1151 if (*psczVolumePathName)
905 PathExitOnFailure(hr, "Failed to allocate memory for known folder."); 1152 {
1153 hr = StrMaxLength(*psczVolumePathName, &cchMax);
1154 PathExitOnFailure(hr, "Failed to get max length of input buffer.");
906 1155
907 hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder); 1156 cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
908 PathExitOnFailure(hr, "Failed to get known folder path."); 1157 }
1158 else
1159 {
1160 cchBuffer = MAX_PATH + 1;
1161
1162 hr = StrAlloc(psczVolumePathName, cchBuffer);
1163 PathExitOnFailure(hr, "Failed to allocate space for volume path name.");
1164 }
1165
1166 for (DWORD i = 0; i < dwMaxAttempts; ++i)
1167 {
1168 if (::GetVolumePathNameW(wzFileName, *psczVolumePathName, cchBuffer))
1169 {
1170 break;
1171 }
1172
1173 hr = HRESULT_FROM_WIN32(::GetLastError());
1174 if (HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) != hr && E_INSUFFICIENT_BUFFER != hr ||
1175 (dwMaxAttempts - 1) == i)
1176 {
1177 PathExitWithRootFailure(hr, hr, "Failed to get volume path name of: %ls", wzFileName);
1178 }
1179
1180 cchBuffer *= 2;
1181
1182 hr = StrAlloc(psczVolumePathName, cchBuffer);
1183 PathExitOnFailure(hr, "Failed to re-allocate more space for volume path name.");
1184 }
909 1185
910 hr = PathBackslashTerminate(psczKnownFolder); 1186 hr = PathBackslashTerminate(psczVolumePathName);
911 PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated."); 1187 PathExitOnFailure(hr, "Failed to terminate volume path name with backslash.");
912 1188
913LExit: 1189LExit:
914 return hr; 1190 return hr;
diff --git a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
index 155ca714..ce28beb3 100644
--- a/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/rexutil.cpp
@@ -28,7 +28,7 @@ static ERF verf;
28static FAKE_FILE vrgffFileTable[FILETABLESIZE]; 28static FAKE_FILE vrgffFileTable[FILETABLESIZE];
29static DWORD vcbRes; 29static DWORD vcbRes;
30static LPCBYTE vpbRes; 30static LPCBYTE vpbRes;
31static CHAR vszResource[MAX_PATH]; 31static LPSTR vpszResource = NULL;
32static REX_CALLBACK_WRITE vpfnWrite = NULL; 32static REX_CALLBACK_WRITE vpfnWrite = NULL;
33 33
34static HRESULT vhrLastError = S_OK; 34static HRESULT vhrLastError = S_OK;
@@ -85,6 +85,8 @@ LExit:
85 { 85 {
86 ::FDIDestroy(vhfdi); 86 ::FDIDestroy(vhfdi);
87 vhfdi = NULL; 87 vhfdi = NULL;
88
89 ReleaseNullStr(vpszResource);
88 } 90 }
89 91
90 return hr; 92 return hr;
@@ -101,6 +103,8 @@ extern "C" void RexUninitialize()
101 { 103 {
102 ::FDIDestroy(vhfdi); 104 ::FDIDestroy(vhfdi);
103 vhfdi = NULL; 105 vhfdi = NULL;
106
107 ReleaseNullStr(vpszResource);
104 } 108 }
105} 109}
106 110
@@ -124,12 +128,12 @@ extern "C" HRESULT RexExtract(
124{ 128{
125 Assert(vhfdi); 129 Assert(vhfdi);
126 HRESULT hr = S_OK; 130 HRESULT hr = S_OK;
127 BOOL fResult; 131 BOOL fResult = FALSE;
128 132
129 HRSRC hResInfo = NULL; 133 HRSRC hResInfo = NULL;
130 HANDLE hRes = NULL; 134 HANDLE hRes = NULL;
131 135
132 REX_CALLBACK_STRUCT rcs; 136 REX_CALLBACK_STRUCT rcs = { };
133 137
134 // remember the write callback 138 // remember the write callback
135 vpfnWrite = pfnWrite; 139 vpfnWrite = pfnWrite;
@@ -158,7 +162,7 @@ extern "C" HRESULT RexExtract(
158 // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource); 162 // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource);
159 //} 163 //}
160 164
161 hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource); 165 hr = StrAnsiAllocStringAnsi(&vpszResource, szResource, 0);
162 RexExitOnFailure(hr, "Failed to copy resource name to global."); 166 RexExitOnFailure(hr, "Failed to copy resource name to global.");
163 167
164 // 168 //
@@ -171,7 +175,7 @@ extern "C" HRESULT RexExtract(
171 rcs.pfnProgress = pfnProgress; 175 rcs.pfnProgress = pfnProgress;
172 rcs.pvContext = pvContext; 176 rcs.pvContext = pvContext;
173 177
174 fResult = ::FDICopy(vhfdi, vszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs)); 178 fResult = ::FDICopy(vhfdi, vpszResource, "", 0, RexCallback, NULL, static_cast<void*>(&rcs));
175 if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure 179 if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
176 { 180 {
177 hr = vhrLastError; // TODO: put verf info in trace message here 181 hr = vhrLastError; // TODO: put verf info in trace message here
@@ -227,7 +231,7 @@ static __callback INT_PTR FAR DIAMONDAPI RexOpen(__in_z char FAR *pszFile, int o
227 RexExitOnFailure(hr, "File table exceeded"); 231 RexExitOnFailure(hr, "File table exceeded");
228 } 232 }
229 233
230 if (0 == lstrcmpA(vszResource, pszFile)) 234 if (0 == lstrcmpA(vpszResource, pszFile))
231 { 235 {
232 vrgffFileTable[i].fUsed = TRUE; 236 vrgffFileTable[i].fUsed = TRUE;
233 vrgffFileTable[i].fftType = MEMORY_FILE; 237 vrgffFileTable[i].fftType = MEMORY_FILE;
@@ -436,15 +440,16 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
436 HANDLE hFile = INVALID_HANDLE_VALUE; 440 HANDLE hFile = INVALID_HANDLE_VALUE;
437 441
438 REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv); 442 REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv);
439 LPCSTR sz; 443 LPCSTR sz = NULL;
440 WCHAR wz[MAX_PATH]; 444 LPWSTR pwz = NULL;
441 FILETIME ft; 445 LPWSTR pwzPath = NULL;
446 FILETIME ft = { };
442 int i = 0; 447 int i = 0;
443 448
444 switch (iNotification) 449 switch (iNotification)
445 { 450 {
446 case fdintCOPY_FILE: // beGIN extracting a resource from cabinet 451 case fdintCOPY_FILE: // beGIN extracting a resource from cabinet
447 Assert(pFDINotify->psz1); 452 Assert(pFDINotify->psz1 && prcs);
448 453
449 if (prcs->fStopExtracting) 454 if (prcs->fStopExtracting)
450 { 455 {
@@ -453,55 +458,50 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
453 458
454 // convert params to useful variables 459 // convert params to useful variables
455 sz = static_cast<LPCSTR>(pFDINotify->psz1); 460 sz = static_cast<LPCSTR>(pFDINotify->psz1);
456 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) 461 RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
457 { 462
458 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); 463 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
459 } 464 RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
460 465
461 if (prcs->pfnProgress) 466 if (prcs->pfnProgress)
462 { 467 {
463 hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext); 468 hr = prcs->pfnProgress(TRUE, pwz, prcs->pvContext);
464 if (S_OK != hr) 469 if (S_OK != hr)
465 { 470 {
466 ExitFunction(); 471 ExitFunction();
467 } 472 }
468 } 473 }
469 474
470 if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz)) 475 if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, pwz))
471 { 476 {
472 // get the created date for the resource in the cabinet 477 // get the created date for the resource in the cabinet
473 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft)) 478 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft))
474 { 479 {
475 RexExitWithLastError(hr, "failed to get time for resource: %ls", wz); 480 RexExitWithLastError(hr, "failed to get time for resource: %ls", pwz);
476 } 481 }
477 482
478 WCHAR wzPath[MAX_PATH];
479
480 hr = ::StringCchCopyW(wzPath, countof(wzPath), prcs->pwzExtractDir);
481 RexExitOnFailure(hr, "failed to copy extract directory: %ls for file: %ls", prcs->pwzExtractDir, wz);
482
483 if (L'*' == *prcs->pwzExtract) 483 if (L'*' == *prcs->pwzExtract)
484 { 484 {
485 hr = ::StringCchCatW(wzPath, countof(wzPath), wz); 485 hr = PathConcat(prcs->pwzExtractDir, pwz, &pwzPath);
486 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz); 486 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, pwz);
487 } 487 }
488 else 488 else
489 { 489 {
490 Assert(*prcs->pwzExtractName); 490 Assert(*prcs->pwzExtractName);
491 491
492 hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName); 492 hr = PathConcat(prcs->pwzExtractDir, prcs->pwzExtractName, &pwzPath);
493 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName); 493 RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, prcs->pwzExtractName);
494 } 494 }
495 495
496 // Quickly chop off the file name part of the path to ensure the path exists 496 // Quickly chop off the file name part of the path to ensure the path exists
497 // then put the file name back on the path (by putting the first character 497 // then put the file name back on the path (by putting the first character
498 // back over the null terminator). 498 // back over the null terminator).
499 LPWSTR wzFile = PathFile(wzPath); 499 LPWSTR wzFile = PathFile(pwzPath);
500 WCHAR wzFileFirstChar = *wzFile; 500 WCHAR wzFileFirstChar = *wzFile;
501 *wzFile = L'\0'; 501 *wzFile = L'\0';
502 502
503 hr = DirEnsureExists(wzPath, NULL); 503 hr = DirEnsureExists(pwzPath, NULL);
504 RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath); 504 RexExitOnFailure(hr, "failed to ensure directory: %ls", pwzPath);
505 505
506 hr = S_OK; 506 hr = S_OK;
507 507
@@ -524,10 +524,10 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
524 } 524 }
525 525
526 // open the file 526 // open the file
527 hFile = ::CreateFileW(wzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 527 hFile = ::CreateFileW(pwzPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
528 if (INVALID_HANDLE_VALUE == hFile) 528 if (INVALID_HANDLE_VALUE == hFile)
529 { 529 {
530 RexExitWithLastError(hr, "failed to open file: %ls", wzPath); 530 RexExitWithLastError(hr, "failed to open file: %ls", pwzPath);
531 } 531 }
532 532
533 vrgffFileTable[i].fUsed = TRUE; 533 vrgffFileTable[i].fUsed = TRUE;
@@ -554,20 +554,20 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
554 554
555 break; 555 break;
556 case fdintCLOSE_FILE_INFO: // resource extraction complete 556 case fdintCLOSE_FILE_INFO: // resource extraction complete
557 Assert(pFDINotify->hf && pFDINotify->psz1); 557 Assert(pFDINotify->hf && prcs && pFDINotify->psz1);
558 558
559 // convert params to useful variables 559 // convert params to useful variables
560 sz = static_cast<LPCSTR>(pFDINotify->psz1); 560 sz = static_cast<LPCSTR>(pFDINotify->psz1);
561 if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz))) 561 RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
562 { 562
563 RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz); 563 hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
564 } 564 RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
565 565
566 RexClose(pFDINotify->hf); 566 RexClose(pFDINotify->hf);
567 567
568 if (prcs->pfnProgress) 568 if (prcs->pfnProgress)
569 { 569 {
570 hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext); 570 hr = prcs->pfnProgress(FALSE, pwz, prcs->pvContext);
571 } 571 }
572 572
573 if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going 573 if (S_OK == hr && L'*' == *prcs->pwzExtract) // if everything is okay and we're extracting all files, keep going
@@ -597,5 +597,8 @@ LExit:
597 vhrLastError = hr; 597 vhrLastError = hr;
598 } 598 }
599 599
600 ReleaseStr(pwz);
601 ReleaseStr(pwzPath);
602
600 return (S_OK == hr) ? ipResult : -1; 603 return (S_OK == hr) ? ipResult : -1;
601} 604}
diff --git a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp
index 4eede74f..590c937a 100644
--- a/src/libs/dutil/WixToolset.DUtil/sceutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/sceutil.cpp
@@ -317,7 +317,7 @@ extern "C" HRESULT DAPI SceOpenDatabase(
317{ 317{
318 HRESULT hr = S_OK; 318 HRESULT hr = S_OK;
319 DWORD dwVersionFound = 0; 319 DWORD dwVersionFound = 0;
320 WCHAR wzTempDbFile[MAX_PATH]; 320 LPWSTR sczTempDbFile = NULL;
321 LPCWSTR wzPathToOpen = NULL; 321 LPCWSTR wzPathToOpen = NULL;
322 LPWSTR sczSchemaType = NULL; 322 LPWSTR sczSchemaType = NULL;
323 SCE_DATABASE *pNewSceDatabase = NULL; 323 SCE_DATABASE *pNewSceDatabase = NULL;
@@ -343,16 +343,16 @@ extern "C" HRESULT DAPI SceOpenDatabase(
343 // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now. 343 // TODO: had trouble getting SQL CE to read a file read-only, so we're copying it to a temp path for now.
344 if (fReadOnly) 344 if (fReadOnly)
345 { 345 {
346 hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile)); 346 hr = DirCreateTempPath(PathFile(sczFile), &sczTempDbFile);
347 ExitOnFailure(hr, "Failed to get temp path"); 347 ExitOnFailure(hr, "Failed to get temp path");
348 348
349 hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE); 349 hr = FileEnsureCopy(sczFile, sczTempDbFile, TRUE);
350 ExitOnFailure(hr, "Failed to copy file to temp path"); 350 ExitOnFailure(hr, "Failed to copy file to temp path");
351 351
352 hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, (LPCWSTR)wzTempDbFile, 0); 352 hr = StrAllocString(&pNewSceDatabaseInternal->sczTempDbFile, sczTempDbFile, 0);
353 ExitOnFailure(hr, "Failed to copy temp db file path"); 353 ExitOnFailure(hr, "Failed to copy temp db file path");
354 354
355 wzPathToOpen = (LPCWSTR)wzTempDbFile; 355 wzPathToOpen = sczTempDbFile;
356 } 356 }
357 else 357 else
358 { 358 {
@@ -424,6 +424,7 @@ extern "C" HRESULT DAPI SceOpenDatabase(
424LExit: 424LExit:
425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal); 425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 ReleaseStr(sczSchemaType); 426 ReleaseStr(sczSchemaType);
427 ReleaseStr(sczTempDbFile);
427 ReleaseDatabase(pNewSceDatabase); 428 ReleaseDatabase(pNewSceDatabase);
428 429
429 return hr; 430 return hr;
diff --git a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
index 2eb9a52a..72a0e5ce 100644
--- a/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/shelutil.cpp
@@ -9,6 +9,7 @@
9#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) 9#define ShelExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
10#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) 10#define ShelExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
11#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) 11#define ShelExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
12#define ShelExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_SHELUTIL, x, e, s, __VA_ARGS__)
12#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__) 13#define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
13#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__) 14#define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
14#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__) 15#define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
@@ -19,6 +20,10 @@
19 20
20static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW; 21static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
21 22
23static HRESULT DAPI GetFolderFromCsidl(
24 __out_z LPWSTR* psczFolderPath,
25 __in int csidlFolder
26 );
22static HRESULT GetDesktopShellView( 27static HRESULT GetDesktopShellView(
23 __in REFIID riid, 28 __in REFIID riid,
24 __out void **ppv 29 __out void **ppv
@@ -57,7 +62,14 @@ extern "C" HRESULT DAPI ShelExec(
57 ) 62 )
58{ 63{
59 HRESULT hr = S_OK; 64 HRESULT hr = S_OK;
60 SHELLEXECUTEINFOW shExecInfo = {}; 65 SHELLEXECUTEINFOW shExecInfo = { };
66 size_t cchWorkingDirectory = 0;
67
68 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
69 if (wzWorkingDirectory && FAILED(::StringCchLengthW(wzWorkingDirectory, MAX_PATH - 1, &cchWorkingDirectory)))
70 {
71 wzWorkingDirectory = NULL;
72 }
61 73
62 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); 74 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
63 shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; 75 shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
@@ -159,11 +171,7 @@ LExit:
159} 171}
160 172
161 173
162/******************************************************************** 174static HRESULT DAPI GetFolderFromCsidl(
163 ShelGetFolder() - gets a folder by CSIDL.
164
165*******************************************************************/
166extern "C" HRESULT DAPI ShelGetFolder(
167 __out_z LPWSTR* psczFolderPath, 175 __out_z LPWSTR* psczFolderPath,
168 __in int csidlFolder 176 __in int csidlFolder
169 ) 177 )
@@ -185,19 +193,6 @@ LExit:
185} 193}
186 194
187 195
188/********************************************************************
189 ShelGetKnownFolder() - gets a folder by KNOWNFOLDERID.
190
191 Note: return E_NOTIMPL if called on pre-Vista operating systems.
192*******************************************************************/
193#ifndef REFKNOWNFOLDERID
194#define REFKNOWNFOLDERID REFGUID
195#endif
196
197#ifndef KF_FLAG_CREATE
198#define KF_FLAG_CREATE 0x00008000 // Make sure that the folder already exists or create it and apply security specified in folder definition
199#endif
200
201EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)( 196EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
202 REFKNOWNFOLDERID rfid, 197 REFKNOWNFOLDERID rfid,
203 DWORD dwFlags, 198 DWORD dwFlags,
@@ -249,6 +244,181 @@ LExit:
249 return hr; 244 return hr;
250} 245}
251 246
247extern "C" HRESULT DAPI ShelGetFolder(
248 __out_z LPWSTR* psczFolderPath,
249 __in int csidlFolder
250 )
251{
252 HRESULT hr = S_OK;
253 LPWSTR sczPath = NULL;
254 KNOWNFOLDERID rfid = { };
255
256 csidlFolder &= ~CSIDL_FLAG_MASK;
257
258 switch (csidlFolder)
259 {
260 case CSIDL_ADMINTOOLS:
261 rfid = FOLDERID_AdminTools;
262 break;
263 case CSIDL_APPDATA:
264 rfid = FOLDERID_RoamingAppData;
265 break;
266 case CSIDL_CDBURN_AREA:
267 rfid = FOLDERID_CDBurning;
268 break;
269 case CSIDL_COMMON_ADMINTOOLS:
270 rfid = FOLDERID_CommonAdminTools;
271 break;
272 case CSIDL_COMMON_APPDATA:
273 rfid = FOLDERID_ProgramData;
274 break;
275 case CSIDL_COMMON_DESKTOPDIRECTORY:
276 rfid = FOLDERID_PublicDesktop;
277 break;
278 case CSIDL_COMMON_DOCUMENTS:
279 rfid = FOLDERID_PublicDocuments;
280 break;
281 case CSIDL_COMMON_MUSIC:
282 rfid = FOLDERID_PublicMusic;
283 break;
284 case CSIDL_COMMON_OEM_LINKS:
285 rfid = FOLDERID_CommonOEMLinks;
286 break;
287 case CSIDL_COMMON_PICTURES:
288 rfid = FOLDERID_PublicPictures;
289 break;
290 case CSIDL_COMMON_PROGRAMS:
291 rfid = FOLDERID_CommonPrograms;
292 break;
293 case CSIDL_COMMON_STARTMENU:
294 rfid = FOLDERID_CommonStartMenu;
295 break;
296 case CSIDL_COMMON_STARTUP: __fallthrough;
297 case CSIDL_COMMON_ALTSTARTUP:
298 rfid = FOLDERID_CommonStartup;
299 break;
300 case CSIDL_COMMON_TEMPLATES:
301 rfid = FOLDERID_CommonTemplates;
302 break;
303 case CSIDL_COMMON_VIDEO:
304 rfid = FOLDERID_PublicVideos;
305 break;
306 case CSIDL_COOKIES:
307 rfid = FOLDERID_Cookies;
308 break;
309 case CSIDL_DESKTOP:
310 case CSIDL_DESKTOPDIRECTORY:
311 rfid = FOLDERID_Desktop;
312 break;
313 case CSIDL_FAVORITES: __fallthrough;
314 case CSIDL_COMMON_FAVORITES:
315 rfid = FOLDERID_Favorites;
316 break;
317 case CSIDL_FONTS:
318 rfid = FOLDERID_Fonts;
319 break;
320 case CSIDL_HISTORY:
321 rfid = FOLDERID_History;
322 break;
323 case CSIDL_INTERNET_CACHE:
324 rfid = FOLDERID_InternetCache;
325 break;
326 case CSIDL_LOCAL_APPDATA:
327 rfid = FOLDERID_LocalAppData;
328 break;
329 case CSIDL_MYMUSIC:
330 rfid = FOLDERID_Music;
331 break;
332 case CSIDL_MYPICTURES:
333 rfid = FOLDERID_Pictures;
334 break;
335 case CSIDL_MYVIDEO:
336 rfid = FOLDERID_Videos;
337 break;
338 case CSIDL_NETHOOD:
339 rfid = FOLDERID_NetHood;
340 break;
341 case CSIDL_PERSONAL:
342 rfid = FOLDERID_Documents;
343 break;
344 case CSIDL_PRINTHOOD:
345 rfid = FOLDERID_PrintHood;
346 break;
347 case CSIDL_PROFILE:
348 rfid = FOLDERID_Profile;
349 break;
350 case CSIDL_PROGRAM_FILES:
351 rfid = FOLDERID_ProgramFiles;
352 break;
353 case CSIDL_PROGRAM_FILESX86:
354 rfid = FOLDERID_ProgramFilesX86;
355 break;
356 case CSIDL_PROGRAM_FILES_COMMON:
357 rfid = FOLDERID_ProgramFilesCommon;
358 break;
359 case CSIDL_PROGRAM_FILES_COMMONX86:
360 rfid = FOLDERID_ProgramFilesCommonX86;
361 break;
362 case CSIDL_PROGRAMS:
363 rfid = FOLDERID_Programs;
364 break;
365 case CSIDL_RECENT:
366 rfid = FOLDERID_Recent;
367 break;
368 case CSIDL_RESOURCES:
369 rfid = FOLDERID_ResourceDir;
370 break;
371 case CSIDL_RESOURCES_LOCALIZED:
372 rfid = FOLDERID_LocalizedResourcesDir;
373 break;
374 case CSIDL_SENDTO:
375 rfid = FOLDERID_SendTo;
376 break;
377 case CSIDL_STARTMENU:
378 rfid = FOLDERID_StartMenu;
379 break;
380 case CSIDL_STARTUP:
381 case CSIDL_ALTSTARTUP:
382 rfid = FOLDERID_Startup;
383 break;
384 case CSIDL_SYSTEM:
385 rfid = FOLDERID_System;
386 break;
387 case CSIDL_SYSTEMX86:
388 rfid = FOLDERID_SystemX86;
389 break;
390 case CSIDL_TEMPLATES:
391 rfid = FOLDERID_Templates;
392 break;
393 case CSIDL_WINDOWS:
394 rfid = FOLDERID_Windows;
395 break;
396 default:
397 ShelExitWithRootFailure(hr, E_INVALIDARG, "Unknown csidl: %d", csidlFolder);
398 }
399
400 hr = ShelGetKnownFolder(&sczPath, rfid);
401 if (E_NOTIMPL == hr)
402 {
403 hr = S_FALSE;
404 }
405 ShelExitOnFailure(hr, "Failed to get known folder.");
406
407 if (S_FALSE == hr)
408 {
409 hr = GetFolderFromCsidl(&sczPath, csidlFolder);
410 ShelExitOnFailure(hr, "Failed to get csidl folder.");
411 }
412
413 *psczFolderPath = sczPath;
414 sczPath = NULL;
415
416LExit:
417 ReleaseStr(sczPath);
418
419 return hr;
420}
421
252 422
253// Internal functions. 423// Internal functions.
254 424
@@ -287,7 +457,7 @@ static HRESULT GetDesktopShellView(
287 else if (S_FALSE == hr) 457 else if (S_FALSE == hr)
288 { 458 {
289 //Windows XP 459 //Windows XP
290 hr = SHGetDesktopFolder(&psf); 460 hr = ::SHGetDesktopFolder(&psf);
291 ShelExitOnFailure(hr, "Failed to get desktop folder."); 461 ShelExitOnFailure(hr, "Failed to get desktop folder.");
292 462
293 hr = psf->CreateViewObject(NULL, IID_IShellView, ppv); 463 hr = psf->CreateViewObject(NULL, IID_IShellView, ppv);
diff --git a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
index e9ef1047..109c558c 100644
--- a/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+++ b/src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
@@ -7,6 +7,11 @@ using namespace System::IO;
7using namespace Xunit; 7using namespace Xunit;
8using namespace WixBuildTools::TestSupport; 8using namespace WixBuildTools::TestSupport;
9 9
10// from PathCch.h
11#ifndef PATHCCH_ALLOW_LONG_PATHS
12#define PATHCCH_ALLOW_LONG_PATHS 0x01
13#endif
14
10namespace DutilTests 15namespace DutilTests
11{ 16{
12 public ref class PathUtil 17 public ref class PathUtil
@@ -102,18 +107,54 @@ namespace DutilTests
102 } 107 }
103 108
104 [Fact] 109 [Fact]
110 void PathCanonicalizeForComparisonFallbackTest()
111 {
112 Path2FunctionForceFallback();
113
114 try
115 {
116 PathCanonicalizeForComparisonTestCore(FALSE);
117 }
118 finally
119 {
120 Path2FunctionAllowFallback();
121 }
122 }
123
124 [Fact]
105 void PathCanonicalizeForComparisonTest() 125 void PathCanonicalizeForComparisonTest()
106 { 126 {
127 PathCanonicalizeForComparisonTestCore(TRUE);
128 }
129
130 void PathCanonicalizeForComparisonTestCore(BOOL fLongPathSupported)
131 {
107 HRESULT hr = S_OK; 132 HRESULT hr = S_OK;
108 LPWSTR sczCanonicalized = NULL; 133 LPWSTR sczCanonicalized = NULL;
109 134
110 try 135 try
111 { 136 {
112 hr = PathCanonicalizeForComparison(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized); 137 hr = PathCanonicalizeForComparison(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
113 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); 138 if (!fLongPathSupported)
139 {
140 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
141 }
142 else
143 {
144 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
145 NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
146 }
114 147
115 hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized); 148 hr = PathCanonicalizeForComparison(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", 0, &sczCanonicalized);
116 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr); 149 if (!fLongPathSupported)
150 {
151 Assert::Equal<HRESULT>(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), hr);
152 }
153 else
154 {
155 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
156 NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
157 }
117 158
118 hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized); 159 hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
119 NativeAssert::Succeeded(hr, "Failed to canonicalize path"); 160 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
@@ -238,6 +279,52 @@ namespace DutilTests
238 } 279 }
239 280
240 [Fact] 281 [Fact]
282 void PathAllocCanonicalizePathTest()
283 {
284 HRESULT hr = S_OK;
285 LPWSTR sczCanonicalized = NULL;
286
287 try
288 {
289 hr = PathAllocCanonicalizePath(L"C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
290 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
291 NativeAssert::StringEqual(L"\\\\?\\C:\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
292
293 hr = PathAllocCanonicalizePath(L"abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
294 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
295 NativeAssert::StringEqual(L"abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789\\abcdefghijklomnopqrstuvwxyz0123456789", sczCanonicalized);
296
297 hr = PathAllocCanonicalizePath(L"\\\\server\\share\\..\\..\\otherdir\\unc.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
298 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
299 NativeAssert::StringEqual(L"\\\\otherdir\\unc.exe", sczCanonicalized); // This is surprising.
300
301 hr = PathAllocCanonicalizePath(L"C:\\dir\\subdir\\..\\..\\otherdir\\backslashes.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
302 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
303 NativeAssert::StringEqual(L"C:\\otherdir\\backslashes.exe", sczCanonicalized);
304
305 hr = PathAllocCanonicalizePath(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
306 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
307 NativeAssert::StringEqual(L"C:/dir/subdir/../../otherdir/forwardslashes.exe", sczCanonicalized);
308
309 hr = PathAllocCanonicalizePath(L"\\\\?\\C:\\test\\..\\validlongpath.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
310 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
311 NativeAssert::StringEqual(L"C:\\validlongpath.exe", sczCanonicalized);
312
313 hr = PathAllocCanonicalizePath(L"\\\\?\\test\\..\\invalidlongpath.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
314 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
315 NativeAssert::StringEqual(L"\\\\?\\invalidlongpath.exe", sczCanonicalized);
316
317 hr = PathAllocCanonicalizePath(L"C:\\.\\invalid:pathchars?.exe", PATHCCH_ALLOW_LONG_PATHS, &sczCanonicalized);
318 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
319 NativeAssert::StringEqual(L"C:\\invalid:pathchars?.exe", sczCanonicalized);
320 }
321 finally
322 {
323 ReleaseStr(sczCanonicalized);
324 }
325 }
326
327 [Fact]
241 void PathConcatTest() 328 void PathConcatTest()
242 { 329 {
243 HRESULT hr = S_OK; 330 HRESULT hr = S_OK;
@@ -545,6 +632,25 @@ namespace DutilTests
545 } 632 }
546 633
547 [Fact] 634 [Fact]
635 void PathForCurrentProcessTest()
636 {
637 HRESULT hr = S_OK;
638 LPWSTR sczPath = NULL;
639
640 try
641 {
642 hr = PathForCurrentProcess(&sczPath, NULL);
643 NativeAssert::Succeeded(hr, "Failed to get current process path.");
644
645 WixAssert::StringEqual(System::Diagnostics::Process::GetCurrentProcess()->MainModule->FileName, gcnew String(sczPath), false);
646 }
647 finally
648 {
649 ReleaseStr(sczPath);
650 }
651 }
652
653 [Fact]
548 void PathGetDirectoryTest() 654 void PathGetDirectoryTest()
549 { 655 {
550 HRESULT hr = S_OK; 656 HRESULT hr = S_OK;
@@ -828,6 +934,78 @@ namespace DutilTests
828 } 934 }
829 935
830 [Fact] 936 [Fact]
937 void PathGetTempPathTest()
938 {
939 HRESULT hr = S_OK;
940 LPCWSTR wzEnvName = L"TMP";
941 LPCWSTR wzEnvName2 = L"TEMP";
942 LPCWSTR wzLongTempPath = L"C:\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\\";
943 LPWSTR sczTempPath = NULL;
944 WCHAR wzOriginalTemp[MAX_PATH + 1] = { };
945 WCHAR wzOriginalTemp2[MAX_PATH + 1] = { };
946 DWORD cch = 0;
947 DWORD cch2 = 0;
948 SIZE_T cchTemp = 0;
949 size_t cchTemp2 = 0;
950
951 try
952 {
953 cch = ::GetEnvironmentVariableW(wzEnvName, wzOriginalTemp, countof(wzOriginalTemp));
954 Assert::NotEqual<DWORD>(0, cch);
955
956 if (!::SetEnvironmentVariableW(wzEnvName, wzLongTempPath))
957 {
958 Assert::Equal<DWORD>(0xFFFFFFFF, ::GetLastError());
959 }
960
961 cch2 = ::GetEnvironmentVariableW(wzEnvName2, wzOriginalTemp2, countof(wzOriginalTemp2));
962 Assert::NotEqual<DWORD>(0, cch2);
963
964 hr = PathGetTempPath(&sczTempPath, &cchTemp);
965 NativeAssert::Succeeded(hr, "Failed to get temp path.");
966
967 PathFixedBackslashTerminate(wzOriginalTemp2, countof(wzOriginalTemp2));
968
969 hr = ::StringCchLengthW(wzOriginalTemp2, countof(wzOriginalTemp2), &cchTemp2);
970 NativeAssert::Succeeded(hr, "Failed to get temp path length.");
971
972 NativeAssert::StringEqual(wzOriginalTemp2, sczTempPath);
973 Assert::Equal<SIZE_T>(cchTemp2, cchTemp);
974 }
975 finally
976 {
977 if (cch)
978 {
979 ::SetEnvironmentVariableW(wzEnvName, wzOriginalTemp);
980 }
981
982 ReleaseStr(sczTempPath);
983 }
984 }
985
986 [Fact]
987 void PathGetVolumePathNameTest()
988 {
989 HRESULT hr = S_OK;
990 LPWSTR sczVolumePathName = NULL;
991
992 try
993 {
994 hr = StrAlloc(&sczVolumePathName, 1);
995 NativeAssert::Succeeded(hr, "Failed to alloc volume path name.");
996
997 hr = PathGetVolumePathName(L"C:\\Windows", &sczVolumePathName);
998 NativeAssert::Succeeded(hr, "PathGetVolumePathName failed.");
999
1000 NativeAssert::StringEqual(L"C:\\", sczVolumePathName);
1001 }
1002 finally
1003 {
1004 ReleaseStr(sczVolumePathName);
1005 }
1006 }
1007
1008 [Fact]
831 void PathNormalizeSlashesFixedTest() 1009 void PathNormalizeSlashesFixedTest()
832 { 1010 {
833 HRESULT hr = S_OK; 1011 HRESULT hr = S_OK;
diff --git a/src/test/burn/Directory.wixproj.targets b/src/test/burn/Directory.wixproj.targets
index 17a46e2a..4037e865 100644
--- a/src/test/burn/Directory.wixproj.targets
+++ b/src/test/burn/Directory.wixproj.targets
@@ -7,6 +7,7 @@
7 <WebServerBaseUrl Condition=" '$(WebServerBaseUrl)' == '' ">http://localhost:9999/e2e/</WebServerBaseUrl> 7 <WebServerBaseUrl Condition=" '$(WebServerBaseUrl)' == '' ">http://localhost:9999/e2e/</WebServerBaseUrl>
8 <DefineConstants>TestGroupName=$(TestGroupName);PackageName=$(PackageName);BundleName=$(BundleName);WebServerBaseUrl=$(WebServerBaseUrl);$(DefineConstants)</DefineConstants> 8 <DefineConstants>TestGroupName=$(TestGroupName);PackageName=$(PackageName);BundleName=$(BundleName);WebServerBaseUrl=$(WebServerBaseUrl);$(DefineConstants)</DefineConstants>
9 <DefineConstants Condition=" '$(BA)' != '' ">BA=$(BA);$(DefineConstants)</DefineConstants> 9 <DefineConstants Condition=" '$(BA)' != '' ">BA=$(BA);$(DefineConstants)</DefineConstants>
10 <DefineConstants Condition=" '$(BundleLogDirectory)' != '' ">BundleLogDirectory=$(BundleLogDirectory);$(DefineConstants)</DefineConstants>
10 <DefineConstants Condition=" '$(CabPrefix)' != '' ">CabPrefix=$(CabPrefix);$(DefineConstants)</DefineConstants> 11 <DefineConstants Condition=" '$(CabPrefix)' != '' ">CabPrefix=$(CabPrefix);$(DefineConstants)</DefineConstants>
11 <DefineConstants Condition=" '$(IncludeSoftwareTag)' == 'true' ">SoftwareTag=1;$(DefineConstants)</DefineConstants> 12 <DefineConstants Condition=" '$(IncludeSoftwareTag)' == 'true' ">SoftwareTag=1;$(DefineConstants)</DefineConstants>
12 <DefineConstants Condition=" '$(ProductCode)' != '' ">ProductCode=$(ProductCode);$(DefineConstants)</DefineConstants> 13 <DefineConstants Condition=" '$(ProductCode)' != '' ">ProductCode=$(ProductCode);$(DefineConstants)</DefineConstants>
diff --git a/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wixproj b/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wixproj
new file mode 100644
index 00000000..3686200c
--- /dev/null
+++ b/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wixproj
@@ -0,0 +1,19 @@
1<!-- 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. -->
2<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <OutputType>Bundle</OutputType>
5 <UpgradeCode>{EFCF768F-1B06-4B68-9DE0-9244F8212D31}</UpgradeCode>
6 <BundleLogDirectory>[LocalAppDataFolder]Temp</BundleLogDirectory>
7 </PropertyGroup>
8 <ItemGroup>
9 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 </ItemGroup>
11 <ItemGroup>
12 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
13 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 </ItemGroup>
15 <ItemGroup>
16 <PackageReference Include="WixToolset.Bal.wixext" />
17 <PackageReference Include="WixToolset.NetFx.wixext" />
18 </ItemGroup>
19</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wxs b/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wxs
new file mode 100644
index 00000000..e3872eb1
--- /dev/null
+++ b/src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wxs
@@ -0,0 +1,10 @@
1<!-- 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. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <MsiPackage Compressed="no" Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" />
8 </PackageGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/TestData/LongPathTests/PackageA/PackageA.wixproj b/src/test/burn/TestData/LongPathTests/PackageA/PackageA.wixproj
new file mode 100644
index 00000000..798452e7
--- /dev/null
+++ b/src/test/burn/TestData/LongPathTests/PackageA/PackageA.wixproj
@@ -0,0 +1,10 @@
1<!-- 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. -->
2<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <CabPrefix>a</CabPrefix>
5 <UpgradeCode>{4DC05A2A-382D-4E7D-B6DA-163908396373}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/Templates/Bundle.wxs b/src/test/burn/TestData/Templates/Bundle.wxs
index b211d9c3..612e67f5 100644
--- a/src/test/burn/TestData/Templates/Bundle.wxs
+++ b/src/test/burn/TestData/Templates/Bundle.wxs
@@ -3,10 +3,13 @@
3<?ifndef Version?> 3<?ifndef Version?>
4<?define Version = 1.0.0.0?> 4<?define Version = 1.0.0.0?>
5<?endif?> 5<?endif?>
6<?ifndef BundleLogDirectory?>
7<?define BundleLogDirectory = .?>
8<?endif?>
6 9
7<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"> 10<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
8 <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="yes"> 11 <Bundle Name="~$(var.TestGroupName) - $(var.BundleName)" Version="$(var.Version)" UpgradeCode="$(var.UpgradeCode)" Compressed="yes">
9 <Log Prefix="~$(var.TestGroupName)_$(var.BundleName)" /> 12 <Log Prefix="$(var.BundleLogDirectory)\~$(var.TestGroupName)_$(var.BundleName)" />
10 13
11 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" /> 14 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
12 15
diff --git a/src/test/burn/TestExe/Task.cs b/src/test/burn/TestExe/Task.cs
index 59f774fb..0d283c6c 100644
--- a/src/test/burn/TestExe/Task.cs
+++ b/src/test/burn/TestExe/Task.cs
@@ -2,8 +2,10 @@
2 2
3using System; 3using System;
4using System.Collections.Generic; 4using System.Collections.Generic;
5using System.ComponentModel;
5using System.Diagnostics; 6using System.Diagnostics;
6using System.IO; 7using System.IO;
8using System.Runtime.InteropServices;
7using Microsoft.Win32; 9using Microsoft.Win32;
8 10
9namespace TestExe 11namespace TestExe
@@ -151,6 +153,67 @@ namespace TestExe
151 } 153 }
152 } 154 }
153 155
156 public class DeleteManifestsTask : Task
157 {
158 public DeleteManifestsTask(string Data) : base(Data) { }
159
160 public override void RunTask()
161 {
162 string filePath = System.Environment.ExpandEnvironmentVariables(this.data);
163 IntPtr type = new IntPtr(24); //RT_MANIFEST
164 IntPtr name = new IntPtr(1); //CREATEPROCESS_MANIFEST_RESOURCE_ID
165 DeleteResource(filePath, type, name, 1033);
166 }
167
168 private static void DeleteResource(string filePath, IntPtr type, IntPtr name, ushort language, bool throwOnError = false)
169 {
170 bool discard = true;
171 IntPtr handle = BeginUpdateResourceW(filePath, false);
172 try
173 {
174 if (handle == IntPtr.Zero)
175 {
176 throw new Win32Exception();
177 }
178
179 if (!UpdateResourceW(handle, type, name, language, IntPtr.Zero, 0))
180 {
181 throw new Win32Exception();
182 }
183
184 discard = false;
185 }
186 catch
187 {
188 if (throwOnError)
189 {
190 throw;
191 }
192 }
193 finally
194 {
195 if (handle != IntPtr.Zero)
196 {
197 if (!EndUpdateResourceW(handle, discard) && throwOnError)
198 {
199 throw new Win32Exception();
200 }
201 }
202 }
203 }
204
205 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
206 private extern static IntPtr BeginUpdateResourceW(string fileName, [MarshalAs(UnmanagedType.Bool)] bool deleteExistingResources);
207
208 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
209 [return: MarshalAs(UnmanagedType.Bool)]
210 private extern static bool UpdateResourceW(IntPtr hUpdate, IntPtr type, IntPtr name, ushort language, IntPtr pData, uint cb);
211
212 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
213 [return: MarshalAs(UnmanagedType.Bool)]
214 private extern static bool EndUpdateResourceW(IntPtr hUpdate, [MarshalAs(UnmanagedType.Bool)] bool discard);
215 }
216
154 public class TaskParser 217 public class TaskParser
155 { 218 {
156 219
@@ -197,6 +260,10 @@ namespace TestExe
197 t = new FileExistsTask(args[i + 1]); 260 t = new FileExistsTask(args[i + 1]);
198 tasks.Add(t); 261 tasks.Add(t);
199 break; 262 break;
263 case "/dm":
264 t = new DeleteManifestsTask(args[i + 1]);
265 tasks.Add(t);
266 break;
200#if NET35 267#if NET35
201 case "/pinfo": 268 case "/pinfo":
202 t = new ProcessInfoTask(args[i + 1]); 269 t = new ProcessInfoTask(args[i + 1]);
diff --git a/src/test/burn/WixTestTools/BundleInstaller.cs b/src/test/burn/WixTestTools/BundleInstaller.cs
index 5551d3c0..0f2cfa8f 100644
--- a/src/test/burn/WixTestTools/BundleInstaller.cs
+++ b/src/test/burn/WixTestTools/BundleInstaller.cs
@@ -28,6 +28,8 @@ namespace WixTestTools
28 28
29 public int? AlternateExitCode { get; set; } 29 public int? AlternateExitCode { get; set; }
30 30
31 public string LogDirectory { get; set; }
32
31 public int? LastExitCode { get; set; } 33 public int? LastExitCode { get; set; }
32 34
33 /// <summary> 35 /// <summary>
@@ -194,7 +196,7 @@ namespace WixTestTools
194 sb.Append(" -quiet"); 196 sb.Append(" -quiet");
195 197
196 // Generate the log file name. 198 // Generate the log file name.
197 string logFile = Path.Combine(Path.GetTempPath(), String.Format("{0}_{1}_{2:yyyyMMddhhmmss}_{4}_{3}.log", this.TestGroupName, this.TestName, DateTime.UtcNow, Path.GetFileNameWithoutExtension(this.Bundle), mode)); 199 string logFile = Path.Combine(this.LogDirectory ?? Path.GetTempPath(), String.Format("{0}_{1}_{2:yyyyMMddhhmmss}_{4}_{3}.log", this.TestGroupName, this.TestName, DateTime.UtcNow, Path.GetFileNameWithoutExtension(this.Bundle), mode));
198 sb.AppendFormat(" -log \"{0}\"", logFile); 200 sb.AppendFormat(" -log \"{0}\"", logFile);
199 201
200 // Set operation. 202 // Set operation.
diff --git a/src/test/burn/WixTestTools/MSIExec.cs b/src/test/burn/WixTestTools/MSIExec.cs
index a10a48d6..5f57da7b 100644
--- a/src/test/burn/WixTestTools/MSIExec.cs
+++ b/src/test/burn/WixTestTools/MSIExec.cs
@@ -110,7 +110,7 @@ namespace WixTestTools
110 this.NoRestart = true; 110 this.NoRestart = true;
111 this.ForceRestart = false; 111 this.ForceRestart = false;
112 this.PromptRestart = false; 112 this.PromptRestart = false;
113 this.LogFile = string.Empty; 113 this.LogFile = String.Empty;
114 this.LoggingOptions = MSIExecLoggingOptions.VOICEWARMUP; 114 this.LoggingOptions = MSIExecLoggingOptions.VOICEWARMUP;
115 this.OtherArguments = String.Empty; 115 this.OtherArguments = String.Empty;
116 } 116 }
@@ -230,14 +230,14 @@ namespace WixTestTools
230 } 230 }
231 231
232 // logfile and logging options 232 // logfile and logging options
233 if (0 != loggingOptionsString.Length || !string.IsNullOrEmpty(this.LogFile)) 233 if (0 != loggingOptionsString.Length || !String.IsNullOrEmpty(this.LogFile))
234 { 234 {
235 arguments.Append(" /l"); 235 arguments.Append(" /l");
236 if (0 != loggingOptionsString.Length) 236 if (0 != loggingOptionsString.Length)
237 { 237 {
238 arguments.AppendFormat("{0} ", loggingOptionsString); 238 arguments.AppendFormat("{0} ", loggingOptionsString);
239 } 239 }
240 if (!string.IsNullOrEmpty(this.LogFile)) 240 if (!String.IsNullOrEmpty(this.LogFile))
241 { 241 {
242 arguments.AppendFormat(" \"{0}\" ", this.LogFile); 242 arguments.AppendFormat(" \"{0}\" ", this.LogFile);
243 } 243 }
@@ -268,7 +268,7 @@ namespace WixTestTools
268 }; 268 };
269 269
270 // product 270 // product
271 if (!string.IsNullOrEmpty(this.Product)) 271 if (!String.IsNullOrEmpty(this.Product))
272 { 272 {
273 arguments.AppendFormat(" \"{0}\" ", this.Product); 273 arguments.AppendFormat(" \"{0}\" ", this.Product);
274 } 274 }
@@ -311,6 +311,12 @@ namespace WixTestTools
311 ERROR_CALL_NOT_IMPLEMENTED = 120, 311 ERROR_CALL_NOT_IMPLEMENTED = 120,
312 312
313 /// <summary> 313 /// <summary>
314 /// ERROR_FILENAME_EXCED_RANGE 206
315 /// The filename or extension is too long.
316 /// </summary>
317 ERROR_FILENAME_EXCED_RANGE = 206,
318
319 /// <summary>
314 /// ERROR_APPHELP_BLOCK 1259 320 /// ERROR_APPHELP_BLOCK 1259
315 /// If Windows Installer determines a product may be incompatible with the current operating system, 321 /// If Windows Installer determines a product may be incompatible with the current operating system,
316 /// it displays a dialog box informing the user and asking whether to try to install anyway. 322 /// it displays a dialog box informing the user and asking whether to try to install anyway.
diff --git a/src/test/burn/WixTestTools/TestTool.cs b/src/test/burn/WixTestTools/TestTool.cs
index eb77c75b..79e7004c 100644
--- a/src/test/burn/WixTestTools/TestTool.cs
+++ b/src/test/burn/WixTestTools/TestTool.cs
@@ -229,7 +229,7 @@ namespace WixTestTools
229 returnValue.AppendLine("Tool run result:"); 229 returnValue.AppendLine("Tool run result:");
230 returnValue.AppendLine("----------------"); 230 returnValue.AppendLine("----------------");
231 returnValue.AppendLine("Command:"); 231 returnValue.AppendLine("Command:");
232 returnValue.AppendLine($"\"{result.StartInfo.FileName}\" {result.StartInfo.Arguments}"); 232 returnValue.AppendLine($"\"{result.FileName}\" {result.Arguments}");
233 returnValue.AppendLine(); 233 returnValue.AppendLine();
234 returnValue.AppendLine("Standard Output:"); 234 returnValue.AppendLine("Standard Output:");
235 foreach (var line in result.StandardOutput ?? new string[0]) 235 foreach (var line in result.StandardOutput ?? new string[0])
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs
new file mode 100644
index 00000000..ba793d7a
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs
@@ -0,0 +1,298 @@
1// 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.
2
3namespace WixToolsetTest.BurnE2E
4{
5 using System;
6 using System.ComponentModel;
7 using System.IO;
8 using System.Runtime.InteropServices;
9 using Microsoft.Win32;
10 using WixBuildTools.TestSupport;
11 using WixTestTools;
12 using WixToolset.Mba.Core;
13 using Xunit;
14 using Xunit.Abstractions;
15
16 public class LongPathTests : BurnE2ETests
17 {
18 public LongPathTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
19
20 [RuntimeFact]
21 public void CanInstallAndUninstallSimpleBundle_x86_wixstdba()
22 {
23 this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleA");
24 }
25
26 [RuntimeFact]
27 public void CanInstallAndUninstallSimpleBundle_x86_testba()
28 {
29 this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleB");
30 }
31
32 [RuntimeFact]
33 public void CanInstallAndUninstallSimpleBundle_x86_dnctestba()
34 {
35 this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleC");
36 }
37
38 [RuntimeFact]
39 public void CanInstallAndUninstallSimpleBundle_x86_wixba()
40 {
41 this.CanInstallAndUninstallSimpleBundle("PackageA", "BundleD");
42 }
43
44 [RuntimeFact]
45 public void CanInstallAndUninstallSimpleBundle_x64_wixstdba()
46 {
47 this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleA_x64");
48 }
49
50 [RuntimeFact]
51 public void CanInstallAndUninstallSimplePerUserBundle_x64_wixstdba()
52 {
53 this.CanInstallAndUninstallSimpleBundle("PackageApu_x64", "BundleApu_x64", "PackagePerUser.wxs", unchecked((int)0xc0000005));
54 }
55
56 [RuntimeFact]
57 public void CanInstallAndUninstallSimpleBundle_x64_testba()
58 {
59 this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleB_x64");
60 }
61
62 [RuntimeFact]
63 public void CanInstallAndUninstallSimpleBundle_x64_dnctestba()
64 {
65 this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleC_x64");
66 }
67
68 [RuntimeFact]
69 public void CanInstallAndUninstallSimpleBundle_x64_dncwixba()
70 {
71 this.CanInstallAndUninstallSimpleBundle("PackageA_x64", "BundleD_x64");
72 }
73
74 private void CanInstallAndUninstallSimpleBundle(string packageName, string bundleName, string fileName = "Package.wxs", int? alternateExitCode = null)
75 {
76 var package = this.CreatePackageInstaller(Path.Combine("..", "BasicFunctionalityTests", packageName));
77
78 var bundle = this.CreateBundleInstaller(Path.Combine("..", "BasicFunctionalityTests", bundleName));
79 bundle.AlternateExitCode = alternateExitCode;
80
81 using var dfs = new DisposableFileSystem();
82 var baseFolder = GetLongPath(dfs.GetFolder());
83
84 var packageSourceCodeInstalled = package.GetInstalledFilePath(fileName);
85
86 // Source file should *not* be installed
87 Assert.False(File.Exists(packageSourceCodeInstalled), $"{packageName} payload should not be there on test start: {packageSourceCodeInstalled}");
88
89 var bundleFileInfo = new FileInfo(bundle.Bundle);
90 var bundleCopiedPath = Path.Combine(baseFolder, bundleFileInfo.Name);
91 bundleFileInfo.CopyTo(bundleCopiedPath);
92
93 bundle.Install(bundleCopiedPath);
94 bundle.VerifyRegisteredAndInPackageCache();
95
96 // Source file should be installed
97 Assert.True(File.Exists(packageSourceCodeInstalled), $"Should have found {packageName} payload installed at: {packageSourceCodeInstalled}");
98
99 if (alternateExitCode == bundle.LastExitCode)
100 {
101 WixAssert.Skip($"Install exited with {bundle.LastExitCode}");
102 }
103
104 bundle.Uninstall(bundleCopiedPath);
105
106 // Source file should *not* be installed
107 Assert.False(File.Exists(packageSourceCodeInstalled), $"{packageName} payload should have been removed by uninstall from: {packageSourceCodeInstalled}");
108
109 bundle.VerifyUnregisteredAndRemovedFromPackageCache();
110
111 if (alternateExitCode == bundle.LastExitCode)
112 {
113 WixAssert.Skip($"Uninstall exited with {bundle.LastExitCode}");
114 }
115 }
116
117 [RuntimeFact]
118 public void CanLayoutNonCompressedBundleToLongPath()
119 {
120 var nonCompressedBundle = this.CreateBundleInstaller("NonCompressedBundle");
121 var testBAController = this.CreateTestBAController();
122
123 testBAController.SetPackageRequestedState("NetFx48Web", RequestState.None);
124
125 using var dfs = new DisposableFileSystem();
126 var layoutDirectory = GetLongPath(dfs.GetFolder());
127
128 nonCompressedBundle.Layout(layoutDirectory);
129 nonCompressedBundle.VerifyUnregisteredAndRemovedFromPackageCache();
130
131 Assert.True(File.Exists(Path.Combine(layoutDirectory, "NonCompressedBundle.exe")));
132 Assert.True(File.Exists(Path.Combine(layoutDirectory, "PackageA.msi")));
133 Assert.True(File.Exists(Path.Combine(layoutDirectory, "1a.cab")));
134 Assert.False(File.Exists(Path.Combine(layoutDirectory, @"redist\ndp48-web.exe")));
135 }
136
137 [RuntimeFact]
138 public void CanInstallNonCompressedBundleWithLongTempPath()
139 {
140 this.InstallNonCompressedBundle(longTemp: true, useOriginalTempForLog: true);
141 }
142
143 [RuntimeFact]
144 public void CannotInstallNonCompressedBundleWithLongPackageCachePath()
145 {
146 var installLogPath = this.InstallNonCompressedBundle((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE, longPackageCache: true);
147 Assert.True(LogVerifier.MessageInLogFile(installLogPath, @"Error 0x80070643: Failed to install MSI package"));
148 }
149
150 [RuntimeFact]
151 public void CannotInstallNonCompressedBundleWithLongWorkingPath()
152 {
153 var installLogPath = this.InstallNonCompressedBundle((int)MSIExec.MSIExecReturnCode.ERROR_FILENAME_EXCED_RANGE | unchecked((int)0x80070000), longWorkingPath: true);
154 Assert.True(LogVerifier.MessageInLogFile(installLogPath, @"Error 0x800700ce: Failed to load BA DLL"));
155 }
156
157 public string InstallNonCompressedBundle(int expectedExitCode = 0, bool longTemp = false, bool useOriginalTempForLog = false, bool longWorkingPath = false, bool longPackageCache = false, int? alternateExitCode = null)
158 {
159 var deletePolicyKey = false;
160 string originalEngineWorkingDirectoryValue = null;
161 string originalPackageCacheValue = null;
162 var originalTemp = Environment.GetEnvironmentVariable("TMP");
163 var packageA = this.CreatePackageInstaller("PackageA");
164 var nonCompressedBundle = this.CreateBundleInstaller("NonCompressedBundle");
165 var policyPath = nonCompressedBundle.GetFullBurnPolicyRegistryPath();
166 string installLogPath = null;
167
168 try
169 {
170 using var dfs = new DisposableFileSystem();
171 var originalBaseFolder = dfs.GetFolder();
172 var baseFolder = GetLongPath(originalBaseFolder);
173 var sourceFolder = Path.Combine(baseFolder, "source");
174 var workingFolder = Path.Combine(baseFolder, "working");
175 var tempFolder = Path.Combine(originalBaseFolder, new string('d', 260 - originalBaseFolder.Length - 2));
176 var packageCacheFolder = Path.Combine(baseFolder, "package cache");
177
178 var copyResult = TestDataFolderFileSystem.RobocopyFolder(this.TestContext.TestDataFolder, sourceFolder);
179 Assert.True(copyResult.ExitCode >= 0 && copyResult.ExitCode < 8, $"Exit code: {copyResult.ExitCode}\r\nOutput: {String.Join("\r\n", copyResult.StandardOutput)}\r\nError: {String.Join("\r\n", copyResult.StandardError)}");
180
181 var bundleFileInfo = new FileInfo(nonCompressedBundle.Bundle);
182 var bundleCopiedPath = Path.Combine(sourceFolder, bundleFileInfo.Name);
183
184 var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true);
185 if (policyKey == null)
186 {
187 policyKey = Registry.LocalMachine.CreateSubKey(policyPath, writable: true);
188 deletePolicyKey = true;
189 }
190
191 using (policyKey)
192 {
193 originalEngineWorkingDirectoryValue = policyKey.GetValue("EngineWorkingDirectory") as string;
194 originalPackageCacheValue = policyKey.GetValue("PackageCache") as string;
195
196 if (longWorkingPath)
197 {
198 policyKey.SetValue("EngineWorkingDirectory", workingFolder);
199 }
200
201 if (longPackageCache)
202 {
203 policyKey.SetValue("PackageCache", packageCacheFolder);
204 }
205 }
206
207 if (longTemp)
208 {
209 Environment.SetEnvironmentVariable("TMP", tempFolder);
210
211 if (useOriginalTempForLog)
212 {
213 nonCompressedBundle.LogDirectory = originalTemp;
214 }
215 }
216
217 try
218 {
219 nonCompressedBundle.AlternateExitCode = alternateExitCode;
220 installLogPath = nonCompressedBundle.Install(bundleCopiedPath, expectedExitCode);
221
222 if (alternateExitCode == nonCompressedBundle.LastExitCode)
223 {
224 WixAssert.Skip($"Install exited with {nonCompressedBundle.LastExitCode}");
225 }
226 }
227 finally
228 {
229 TestDataFolderFileSystem.RobocopyFolder(tempFolder, originalTemp);
230 }
231
232 installLogPath = Path.Combine(originalTemp, Path.GetFileName(installLogPath));
233
234 if (expectedExitCode == 0)
235 {
236 var registration = nonCompressedBundle.VerifyRegisteredAndInPackageCache();
237 packageA.VerifyInstalled(true);
238
239 nonCompressedBundle.Uninstall(registration.CachePath);
240
241 if (alternateExitCode == nonCompressedBundle.LastExitCode)
242 {
243 WixAssert.Skip($"Uninstall exited with {nonCompressedBundle.LastExitCode}");
244 }
245 }
246
247 nonCompressedBundle.VerifyUnregisteredAndRemovedFromPackageCache();
248 packageA.VerifyInstalled(false);
249
250 return installLogPath;
251 }
252 finally
253 {
254 Environment.SetEnvironmentVariable("TMP", originalTemp);
255
256 if (deletePolicyKey)
257 {
258 Registry.LocalMachine.DeleteSubKeyTree(policyPath);
259 }
260 else
261 {
262 using (var policyKey = Registry.LocalMachine.OpenSubKey(policyPath, writable: true))
263 {
264 policyKey?.SetValue("EngineWorkingDirectory", originalEngineWorkingDirectoryValue);
265 policyKey?.SetValue("PackageCache", originalPackageCacheValue);
266 }
267 }
268 }
269 }
270
271 private static string GetLongPath(string baseFolder)
272 {
273 Directory.CreateDirectory(baseFolder);
274
275 // Try to create a directory that is longer than MAX_PATH but without the \\?\ prefix to detect OS support for long paths.
276 // Need to PInvoke CreateDirectoryW directly because .NET methods will append the \\?\ prefix.
277 foreach (var c in new char[] { 'a', 'b', 'c' })
278 {
279 baseFolder = Path.Combine(baseFolder, new string(c, 100));
280 if (!CreateDirectoryW(baseFolder, IntPtr.Zero))
281 {
282 int lastError = Marshal.GetLastWin32Error();
283 if (lastError == 206)
284 {
285 WixAssert.Skip($"MAX_PATH is being enforced ({baseFolder})");
286 }
287 throw new Win32Exception(lastError);
288 }
289 }
290
291 return baseFolder;
292 }
293
294 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
295 [return: MarshalAs(UnmanagedType.Bool)]
296 private extern static bool CreateDirectoryW(string lpPathName, IntPtr lpSecurityAttributes);
297 }
298}
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/testhost.longpathaware.manifest b/src/test/burn/WixToolsetTest.BurnE2E/testhost.longpathaware.manifest
new file mode 100644
index 00000000..a56ab91a
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/testhost.longpathaware.manifest
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<!-- 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. -->
3
4
5<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
6 <application xmlns="urn:schemas-microsoft-com:asm.v3">
7 <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
8 <ws2:longPathAware>true</ws2:longPathAware>
9 </windowsSettings>
10 </application>
11</assembly>
diff --git a/src/test/burn/test_burn.cmd b/src/test/burn/test_burn.cmd
index 83401614..80467f39 100644
--- a/src/test/burn/test_burn.cmd
+++ b/src/test/burn/test_burn.cmd
@@ -9,11 +9,16 @@
9@if /i "%1"=="test" set RuntimeTestsEnabled=true 9@if /i "%1"=="test" set RuntimeTestsEnabled=true
10@if not "%1"=="" shift & goto parse_args 10@if not "%1"=="" shift & goto parse_args
11 11
12@set _B=%~dp0..\..\..\build\IntegrationBurn\%_C%
13
12@echo Burn integration tests %_C% 14@echo Burn integration tests %_C%
13 15
14msbuild -t:Build -Restore -p:Configuration=%_C% -warnaserror -bl:%_L%\test_burn_build.binlog || exit /b 16msbuild -t:Build -Restore -p:Configuration=%_C% -warnaserror -bl:%_L%\test_burn_build.binlog || exit /b
15msbuild -t:Build -Restore TestData\TestData.proj -p:Configuration=%_C% -m -bl:%_L%\test_burn_data_build.binlog || exit /b 17msbuild -t:Build -Restore TestData\TestData.proj -p:Configuration=%_C% -m -bl:%_L%\test_burn_data_build.binlog || exit /b
16 18
19"%_B%\net35\win-x86\testexe.exe" /dm "%_B%\netcoreapp3.1\testhost.exe"
20mt.exe -manifest "WixToolsetTest.BurnE2E\testhost.longpathaware.manifest" -updateresource:"%_B%\netcoreapp3.1\testhost.exe"
21
17@if not "%RuntimeTestsEnabled%"=="true" goto :LExit 22@if not "%RuntimeTestsEnabled%"=="true" goto :LExit
18 23
19dotnet test -c %_C% --no-build WixToolsetTest.BurnE2E -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.BurnE2E.trx" || exit /b 24dotnet test -c %_C% --no-build WixToolsetTest.BurnE2E -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.BurnE2E.trx" || exit /b
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index 672bb235..67a6cb4a 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -231,6 +231,10 @@ namespace WixToolset.Core.Burn.Bundles
231 writer.WriteEndElement(); 231 writer.WriteEndElement();
232 } 232 }
233 233
234 writer.WriteStartElement("longPathAware", ws2016Namespace);
235 writer.WriteString("true");
236 writer.WriteEndElement();
237
234 writer.WriteEndElement(); // </windowSettings> 238 writer.WriteEndElement(); // </windowSettings>
235 writer.WriteEndElement(); // </application> 239 writer.WriteEndElement(); // </application>
236 } 240 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
index bc7fe7a4..a281ad0f 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
@@ -169,7 +169,10 @@ namespace WixToolsetTest.CoreIntegration
169 "<dependency><dependentAssembly><assemblyIdentity name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"x86\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" type=\"win32\" /></dependentAssembly></dependency>" + 169 "<dependency><dependentAssembly><assemblyIdentity name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"x86\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" type=\"win32\" /></dependentAssembly></dependency>" +
170 "<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\"><application><supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" /><supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" /><supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" /><supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" /><supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" /></application></compatibility>" + 170 "<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\"><application><supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" /><supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" /><supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" /><supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" /><supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" /></application></compatibility>" +
171 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" + 171 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" +
172 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings><dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness></windowsSettings></application>" + 172 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings>" +
173 "<dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>" +
174 "<longPathAware xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">true</longPathAware>" +
175 "</windowsSettings></application>" +
173 "</assembly>", actualManifestData); 176 "</assembly>", actualManifestData);
174 } 177 }
175 } 178 }
@@ -215,7 +218,10 @@ namespace WixToolsetTest.CoreIntegration
215 "<dependency><dependentAssembly><assemblyIdentity name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"amd64\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" type=\"win32\" /></dependentAssembly></dependency>" + 218 "<dependency><dependentAssembly><assemblyIdentity name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"amd64\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" type=\"win32\" /></dependentAssembly></dependency>" +
216 "<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\"><application><supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" /><supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" /><supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" /><supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" /><supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" /></application></compatibility>" + 219 "<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\"><application><supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" /><supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" /><supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" /><supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" /><supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" /></application></compatibility>" +
217 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" + 220 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" +
218 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings><dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness></windowsSettings></application>" + 221 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings>" +
222 "<dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>" +
223 "<longPathAware xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">true</longPathAware>" +
224 "</windowsSettings></application>" +
219 "</assembly>", actualManifestData); 225 "</assembly>", actualManifestData);
220 226
221 var extractResult = BundleExtractor.ExtractAllContainers(null, exePath, baFolderPath, attachedFolderPath, extractFolderPath); 227 var extractResult = BundleExtractor.ExtractAllContainers(null, exePath, baFolderPath, attachedFolderPath, extractFolderPath);