@joebigelow / wix-1 / commits / eb53852d

Add longPathAware to Burn manifest to support long paths.

Fixes 3455

Sean Hall committed Jun 24, 2022 at 12:28 UTC eb53852d7ae6838e54525eb57df1d8ce8a722f9b
41 files changed +1890 -392
src/burn/engine/approvedexe.cpp
+9
@@ -148,6 +148,7 @@ extern "C" HRESULT ApprovedExesLaunch(
148 LPWSTR sczCommand = NULL;
149 LPWSTR sczCommandObfuscated = NULL;
150 LPWSTR sczExecutableDirectory = NULL;
151 + size_t cchExecutableDirectory = 0;
152 STARTUPINFOW si = { };
153 PROCESS_INFORMATION pi = { };
154
@@ -177,9 +178,17 @@ extern "C" HRESULT ApprovedExesLaunch(
178 // Try to get the directory of the executable so we can set the current directory of the process to help those executables
179 // that expect stuff to be relative to them. Best effort only.
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 +
187 if (FAILED(hr))
188 {
189 ReleaseNullStr(sczExecutableDirectory);
190 +
191 + hr = S_OK;
192 }
193
194 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
src/burn/engine/cache.cpp
+3 -3
@@ -174,7 +174,7 @@ extern "C" HRESULT CacheInitialize(
174
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.
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");
179
180 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultMachinePackageCache);
@@ -210,7 +210,7 @@ extern "C" HRESULT CacheInitialize(
210 pCache->fCustomMachinePackageCache = !fPathEqual;
211
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");
215
216 hr = PathConcat(sczAppData, PACKAGE_CACHE_FOLDER_NAME, &pCache->sczDefaultUserPackageCache);
@@ -1469,7 +1469,7 @@ static HRESULT CalculateWorkingFolders(
1469 HRESULT hr = S_OK;
1470 LPWSTR sczBaseAcquisitionPath = NULL;
1471
1472 - hr = PathGetTempPath(&sczBaseAcquisitionPath);
1472 + hr = PathGetTempPath(&sczBaseAcquisitionPath, NULL);
1473 ExitOnFailure(hr, "Failed to get temp folder path for acquisition folder base.");
1474
1475 hr = PathBackslashTerminate(&sczBaseAcquisitionPath);
src/burn/engine/engine.cpp
+1 -1
@@ -486,7 +486,7 @@ static HRESULT RunUntrusted(
486
487 si.cb = sizeof(si);
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 {
491 ExitWithLastError(hr, "Failed to launch clean room process: %ls", sczFullCommandLine);
492 }
src/burn/engine/exeengine.cpp
+7
@@ -695,6 +695,7 @@ extern "C" HRESULT ExeEngineRunProcess(
695 BOOL fDelayedCancel = FALSE;
696 BOOL fFireAndForget = BURN_PACKAGE_TYPE_EXE == pPackage->type && pPackage->Exe.fFireAndForget;
697 BOOL fInheritHandles = BURN_PACKAGE_TYPE_BUNDLE == pPackage->type;
698 + size_t cchCachedDirectory = 0;
699
700 // Always add user supplied arguments last.
701 if (wzUserArgs)
@@ -703,6 +704,12 @@ extern "C" HRESULT ExeEngineRunProcess(
704 ExitOnFailure(hr, "Failed to append user args.");
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 +
713 // Make the cache location of the executable the current directory to help those executables
714 // that expect stuff to be relative to them.
715 si.cb = sizeof(si);
src/burn/engine/logging.cpp
+7 -11
@@ -211,7 +211,7 @@ extern "C" void LoggingOpenFailed()
211 LPCWSTR* lpStrings = const_cast<LPCWSTR*>(&LOG_FAILED_EVENT_LOG_MESSAGE);
212 WORD wNumStrings = 1;
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))
216 {
217 ExitFunction();
@@ -965,19 +965,14 @@ static HRESULT GetNonSessionSpecificTempFolder(
965 )
966 {
967 HRESULT hr = S_OK;
968 - WCHAR wzTempFolder[MAX_PATH] = { };
968 + LPWSTR sczTempFolder = NULL;
969 SIZE_T cchTempFolder = 0;
970 DWORD dwSessionId = 0;
971 LPWSTR sczSessionId = 0;
972 SIZE_T cchSessionId = 0;
973
974 - if (!::GetTempPathW(countof(wzTempFolder), wzTempFolder))
975 - {
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.");
974 + hr = PathGetTempPath(&sczTempFolder, &cchTempFolder);
975 + ExitOnFailure(hr, "Failed to get temp folder.");
976
977 // If our session id is in the TEMP path then remove that part so we get the non-session
978 // specific temporary folder.
@@ -989,17 +984,18 @@ static HRESULT GetNonSessionSpecificTempFolder(
984 hr = ::StringCchLengthW(sczSessionId, STRSAFE_MAX_CCH, reinterpret_cast<size_t*>(&cchSessionId));
985 ExitOnFailure(hr, "Failed to get length of session id string.");
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)))
988 {
989 cchTempFolder -= cchSessionId;
990 }
991 }
992
998 - hr = StrAllocString(psczNonSessionTempFolder, wzTempFolder, cchTempFolder);
993 + hr = StrAllocString(psczNonSessionTempFolder, sczTempFolder, cchTempFolder);
994 ExitOnFailure(hr, "Failed to copy temp folder.");
995
996 LExit:
997 ReleaseStr(sczSessionId);
998 + ReleaseStr(sczTempFolder);
999
1000 return hr;
1001 }
src/burn/engine/msuengine.cpp
+2 -7
@@ -269,7 +269,6 @@ extern "C" HRESULT MsuEngineExecutePackage(
269 HRESULT hr = S_OK;
270 LPWSTR sczCachedDirectory = NULL;
271 LPWSTR sczMsuPath = NULL;
272 - LPWSTR sczWindowsPath = NULL;
272 LPWSTR sczSystemPath = NULL;
273 LPWSTR sczWusaPath = NULL;
274 LPWSTR sczCommand = NULL;
@@ -294,15 +293,12 @@ extern "C" HRESULT MsuEngineExecutePackage(
293 // get wusa.exe path
294 if (fUseSysNativePath)
295 {
297 - hr = PathGetKnownFolder(CSIDL_WINDOWS, &sczWindowsPath);
298 - ExitOnFailure(hr, "Failed to find Windows directory.");
299 -
300 - hr = PathConcat(sczWindowsPath, L"SysNative\\", &sczSystemPath);
296 + hr = PathSystemWindowsSubdirectory(L"SysNative\\", &sczSystemPath);
297 ExitOnFailure(hr, "Failed to append SysNative directory.");
298 }
299 else
300 {
305 - hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath);
301 + hr = PathGetSystemDirectory(&sczSystemPath);
302 ExitOnFailure(hr, "Failed to find System32 directory.");
303 }
304
@@ -390,7 +386,6 @@ LExit:
386 ReleaseStr(sczCachedDirectory);
387 ReleaseStr(sczMsuPath);
388 ReleaseStr(sczSystemPath);
393 - ReleaseStr(sczWindowsPath);
389 ReleaseStr(sczWusaPath);
390 ReleaseStr(sczCommand);
391 ReleaseStr(sczEscapedKB);
src/burn/engine/userexperience.cpp
+3 -2
@@ -99,6 +99,7 @@ extern "C" HRESULT UserExperienceLoad(
99 HRESULT hr = S_OK;
100 BOOTSTRAPPER_CREATE_ARGS args = { };
101 BOOTSTRAPPER_CREATE_RESULTS results = { };
102 + LPCWSTR wzPath = pUserExperience->payloads.rgPayloads[0].sczLocalFilePath;
103
104 args.cbSize = sizeof(BOOTSTRAPPER_CREATE_ARGS);
105 args.pCommand = pCommand;
@@ -109,8 +110,8 @@ extern "C" HRESULT UserExperienceLoad(
110 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
111
112 // Load BA DLL.
112 - pUserExperience->hUXModule = ::LoadLibraryExW(pUserExperience->payloads.rgPayloads[0].sczLocalFilePath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
113 - ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL.");
113 + pUserExperience->hUXModule = ::LoadLibraryExW(wzPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
114 + ExitOnNullWithLastError(pUserExperience->hUXModule, hr, "Failed to load BA DLL: %ls", wzPath);
115
116 // Get BootstrapperApplicationCreate entry-point.
117 PFN_BOOTSTRAPPER_APPLICATION_CREATE pfnCreate = (PFN_BOOTSTRAPPER_APPLICATION_CREATE)::GetProcAddress(pUserExperience->hUXModule, "BootstrapperApplicationCreate");
src/burn/engine/variable.cpp
+30 -45
@@ -1947,18 +1947,18 @@ static HRESULT InitializeVariableTempFolder(
1947 UNREFERENCED_PARAMETER(dwpData);
1948
1949 HRESULT hr = S_OK;
1950 - WCHAR wzPath[MAX_PATH] = { };
1950 + LPWSTR sczPath = NULL;
1951
1952 - if (!::GetTempPathW(MAX_PATH, wzPath))
1953 - {
1954 - ExitWithLastError(hr, "Failed to get temp path.");
1955 - }
1952 + hr = PathGetTempPath(&sczPath, NULL);
1953 + ExitOnFailure(hr, "Failed to get temp path.");
1954
1955 // set value
1958 - hr = BVariantSetString(pValue, wzPath, 0, FALSE);
1956 + hr = BVariantSetString(pValue, sczPath, 0, FALSE);
1957 ExitOnFailure(hr, "Failed to set variant value.");
1958
1959 LExit:
1960 + ReleaseStr(sczPath);
1961 +
1962 return hr;
1963 }
1964
@@ -1969,7 +1969,7 @@ static HRESULT InitializeVariableSystemFolder(
1969 {
1970 HRESULT hr = S_OK;
1971 BOOL f64 = (BOOL)dwpData;
1972 - WCHAR wzSystemFolder[MAX_PATH + 2] = { };
1972 + LPWSTR sczSystemFolder = NULL;
1973
1974 #if !defined(_WIN64)
1975 BOOL fIsWow64 = FALSE;
@@ -1979,57 +1979,43 @@ static HRESULT InitializeVariableSystemFolder(
1979 {
1980 if (f64)
1981 {
1982 - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1983 - {
1984 - ExitWithLastError(hr, "Failed to get 64-bit system folder.");
1985 - }
1982 + hr = PathGetSystemDirectory(&sczSystemFolder);
1983 + ExitOnFailure(hr, "Failed to get 64-bit system folder.");
1984 }
1985 else
1986 {
1989 - if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
1990 - {
1991 - ExitWithLastError(hr, "Failed to get 32-bit system folder.");
1992 - }
1987 + hr = PathGetSystemWow64Directory(&sczSystemFolder);
1988 + ExitOnFailure(hr, "Failed to get 32-bit system folder.");
1989 }
1990 }
1991 else
1992 {
1993 if (!f64)
1994 {
1999 - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
2000 - {
2001 - ExitWithLastError(hr, "Failed to get 32-bit system folder.");
2002 - }
1995 + hr = PathGetSystemDirectory(&sczSystemFolder);
1996 + ExitOnFailure(hr, "Failed to get 32-bit system folder.");
1997 }
1998 }
1999 #else
2000 if (f64)
2001 {
2008 - if (!::GetSystemDirectoryW(wzSystemFolder, countof(wzSystemFolder)))
2009 - {
2010 - ExitWithLastError(hr, "Failed to get 64-bit system folder.");
2011 - }
2002 + hr = PathGetSystemDirectory(&sczSystemFolder);
2003 + ExitOnFailure(hr, "Failed to get 64-bit system folder.");
2004 }
2005 else
2006 {
2015 - if (!::GetSystemWow64DirectoryW(wzSystemFolder, countof(wzSystemFolder)))
2016 - {
2017 - ExitWithLastError(hr, "Failed to get 32-bit system folder.");
2018 - }
2007 + hr = PathGetSystemWow64Directory(&sczSystemFolder);
2008 + ExitOnFailure(hr, "Failed to get 32-bit system folder.");
2009 }
2010 #endif
2011
2022 - if (*wzSystemFolder)
2023 - {
2024 - hr = PathFixedBackslashTerminate(wzSystemFolder, countof(wzSystemFolder));
2025 - ExitOnFailure(hr, "Failed to backslash terminate system folder.");
2026 - }
2027 -
2012 // set value
2029 - hr = BVariantSetString(pValue, wzSystemFolder, 0, FALSE);
2013 + hr = BVariantSetString(pValue, sczSystemFolder, 0, FALSE);
2014 ExitOnFailure(hr, "Failed to set system folder variant value.");
2015
2016 LExit:
2017 + ReleaseStr(sczSystemFolder);
2018 +
2019 return hr;
2020 }
2021
@@ -2041,26 +2027,25 @@ static HRESULT InitializeVariableWindowsVolumeFolder(
2027 UNREFERENCED_PARAMETER(dwpData);
2028
2029 HRESULT hr = S_OK;
2044 - WCHAR wzWindowsPath[MAX_PATH] = { };
2045 - WCHAR wzVolumePath[MAX_PATH] = { };
2030 + LPWSTR sczWindowsPath = NULL;
2031 + LPWSTR sczVolumePath = NULL;
2032
2033 // get windows directory
2048 - if (!::GetWindowsDirectoryW(wzWindowsPath, countof(wzWindowsPath)))
2049 - {
2050 - ExitWithLastError(hr, "Failed to get windows directory.");
2051 - }
2034 + hr = PathSystemWindowsSubdirectory(NULL, &sczWindowsPath);
2035 + ExitOnFailure(hr, "Failed to get windows directory.");
2036
2037 // get volume path name
2054 - if (!::GetVolumePathNameW(wzWindowsPath, wzVolumePath, MAX_PATH))
2055 - {
2056 - ExitWithLastError(hr, "Failed to get volume path name.");
2057 - }
2038 + hr = PathGetVolumePathName(sczWindowsPath, &sczVolumePath);
2039 + ExitOnFailure(hr, "Failed to get volume path name.");
2040
2041 // set value
2060 - hr = BVariantSetString(pValue, wzVolumePath, 0, FALSE);
2042 + hr = BVariantSetString(pValue, sczVolumePath, 0, FALSE);
2043 ExitOnFailure(hr, "Failed to set variant value.");
2044
2045 LExit:
2046 + ReleaseStr(sczWindowsPath);
2047 + ReleaseStr(sczVolumePath);
2048 +
2049 return hr;
2050 }
2051
src/burn/test/BurnUnitTest/RegistrationTest.cpp
+3 -3
@@ -108,8 +108,8 @@ namespace Bootstrapper
108 TestThrowOnFailure(hr, L"Failed to register bundle.");
109
110 // verify that registration was created
111 - Assert::True(Directory::Exists(cacheDirectory));
112 - Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))));
111 + Assert::True(Directory::Exists(cacheDirectory), "Cache directory didn't exist.");
112 + Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))), "Bundle exe wasn't cached.");
113
114 this->ValidateUninstallKeyResume(Int32(BURN_RESUME_MODE_ACTIVE));
115 this->ValidateRunOnceKeyEntry(cacheExePath);
@@ -119,7 +119,7 @@ namespace Bootstrapper
119 TestThrowOnFailure(hr, L"Failed to unregister bundle.");
120
121 // verify that registration was removed
122 - Assert::False(Directory::Exists(cacheDirectory));
122 + Assert::False(Directory::Exists(cacheDirectory), "Cache directory wasn't removed.");
123
124 this->ValidateUninstallKeyNull(L"Resume");
125 this->ValidateRunOnceKeyString(TEST_BUNDLE_ID, nullptr);
src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
+287 -1
@@ -2,11 +2,16 @@
2
3 namespace WixBuildTools.TestSupport
4 {
5 + using System;
6 using System.Collections.Concurrent;
7 using System.Collections.Generic;
8 + using System.ComponentModel;
9 using System.Diagnostics;
10 using System.IO;
11 + using System.Runtime.InteropServices;
12 using System.Text;
13 + using System.Threading.Tasks;
14 + using Microsoft.Win32.SafeHandles;
15
16 public abstract class ExternalExecutable
17 {
@@ -18,6 +23,125 @@ namespace WixBuildTools.TestSupport
23 }
24
25 protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
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 {
146 var startInfo = new ProcessStartInfo(this.exePath, args)
147 {
@@ -48,7 +172,8 @@ namespace WixBuildTools.TestSupport
172 ExitCode = process.ExitCode,
173 StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
174 StandardOutput = standardOutput.ToArray(),
51 - StartInfo = startInfo,
175 + FileName = this.exePath,
176 + Arguments = args,
177 };
178 }
179 }
@@ -84,5 +209,166 @@ namespace WixBuildTools.TestSupport
209
210 return sb.ToString();
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 + }
373 }
374 }
src/internal/WixBuildTools.TestSupport/ExternalExecutableResult.cs
+3 -1
@@ -12,6 +12,8 @@ namespace WixBuildTools.TestSupport
12
13 public string[] StandardOutput { get; set; }
14
15 - public ProcessStartInfo StartInfo { get; set; }
15 + public string FileName { get; set; }
16 +
17 + public string Arguments { get; set; }
18 }
19 }
src/internal/WixBuildTools.TestSupport/TestDataFolderFileSystem.cs
+1 -1
@@ -33,7 +33,7 @@ namespace WixBuildTools.TestSupport
33 RobocopyFolder(sourceDirectoryPath, this.BaseFolder);
34 }
35
36 - private static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath)
36 + public static ExternalExecutableResult RobocopyFolder(string sourceFolderPath, string destinationFolderPath)
37 {
38 var args = $"\"{sourceFolderPath}\" \"{destinationFolderPath}\" /E /R:1 /W:1";
39 return RobocopyRunner.Execute(args);
src/libs/dutil/WixToolset.DUtil/apputil.cpp
+12 -16
@@ -87,31 +87,27 @@ DAPI_(HRESULT) LoadSystemLibraryWithPath(
87 )
88 {
89 HRESULT hr = S_OK;
90 - DWORD cch = 0;
91 - WCHAR wzPath[MAX_PATH] = { };
90 + LPWSTR sczDirectory = NULL;
91 + LPWSTR sczPath = NULL;
92
93 - cch = ::GetSystemDirectoryW(wzPath, MAX_PATH);
94 - AppExitOnNullWithLastError(cch, hr, "Failed to get the Windows system directory.");
93 + hr = PathGetSystemDirectory(&sczDirectory);
94 + AppExitOnFailure(hr, "Failed to get the Windows system directory.");
95
96 - if (L'\\' != wzPath[cch - 1])
97 - {
98 - hr = ::StringCchCatNW(wzPath, MAX_PATH, L"\\", 1);
99 - AppExitOnRootFailure(hr, "Failed to terminate the string with a backslash.");
100 - }
96 + hr = StrAllocFormatted(&sczPath, L"%ls%ls", sczDirectory, wzModuleName);
97 + AppExitOnFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
98
102 - hr = ::StringCchCatW(wzPath, MAX_PATH, wzModuleName);
103 - AppExitOnRootFailure(hr, "Failed to create the fully-qualified path to %ls.", wzModuleName);
104 -
105 - *phModule = ::LoadLibraryExW(wzPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
106 - AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", wzModuleName);
99 + *phModule = ::LoadLibraryExW(sczPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
100 + AppExitOnNullWithLastError(*phModule, hr, "Failed to load the library %ls.", sczPath);
101
102 if (psczPath)
103 {
110 - hr = StrAllocString(psczPath, wzPath, MAX_PATH);
111 - AppExitOnFailure(hr, "Failed to copy the path to library.");
104 + *psczPath = sczPath;
105 + sczPath = NULL;
106 }
107
108 LExit:
109 + ReleaseStr(sczDirectory);
110 +
111 return hr;
112 }
113
src/libs/dutil/WixToolset.DUtil/cabcutil.cpp
+35 -51
@@ -89,8 +89,8 @@ struct CABC_DATA
89
90 STRINGDICT_HANDLE shDictHandle;
91
92 - WCHAR wzCabinetPath[MAX_PATH];
93 - WCHAR wzEmptyFile[MAX_PATH];
92 + LPWSTR sczCabinetPath;
93 + LPWSTR sczEmptyFile;
94 HANDLE hEmptyFile;
95 DWORD dwLastFileIndex;
96
@@ -197,33 +197,17 @@ extern "C" HRESULT DAPI CabCBegin(
197
198 HRESULT hr = S_OK;
199 CABC_DATA *pcd = NULL;
200 - WCHAR wzTempPath[MAX_PATH] = { };
200
201 C_ASSERT(sizeof(MSIFILEHASHINFO) == 20);
202
204 - WCHAR wzPathBuffer [MAX_PATH] = L"";
205 - size_t cchPathBuffer;
203 + LPWSTR pwzPathBuffer = NULL;
204 if (wzCabDir)
205 {
208 - hr = ::StringCchLengthW(wzCabDir, MAX_PATH, &cchPathBuffer);
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);
206 + hr = StrAllocString(&pwzPathBuffer, wzCabDir, 0);
207 CabcExitOnFailure(hr, "Failed to copy cab directory to buffer");
208
221 - if (L'\\' != wzPathBuffer[cchPathBuffer - 1])
222 - {
223 - hr = ::StringCchCatW(wzPathBuffer, countof(wzPathBuffer), L"\\");
224 - CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
225 - ++cchPathBuffer;
226 - }
209 + hr = PathBackslashTerminate(&pwzPathBuffer);
210 + CabcExitOnFailure(hr, "Failed to cat \\ to end of buffer");
211 }
212
213 pcd = static_cast<CABC_DATA*>(MemAlloc(sizeof(CABC_DATA), TRUE));
@@ -290,33 +274,23 @@ extern "C" HRESULT DAPI CabCBegin(
274 CabcExitWithLastError(hr, "failed to convert cab name to multi-byte");
275 }
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))
278 {
279 CabcExitWithLastError(hr, "failed to convert cab dir to multi-byte");
280 }
281
282 // Remember the path to the cabinet.
299 - hr= ::StringCchCopyW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzPathBuffer);
300 - CabcExitOnFailure(hr, "Failed to copy cabinet path from path: %ls", wzPathBuffer);
301 -
302 - hr = ::StringCchCatW(pcd->wzCabinetPath, countof(pcd->wzCabinetPath), wzCab);
283 + hr = PathConcat(pwzPathBuffer, wzCab, &pcd->sczCabinetPath);
284 CabcExitOnFailure(hr, "Failed to concat to cabinet path cabinet name: %ls", wzCab);
285
286 // Get the empty file to use as the blank marker for duplicates.
306 - if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
307 - {
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 - }
287 + hr = DirCreateTempPath(L"WSC", &pcd->sczEmptyFile);
288 + CabcExitOnFailure(hr, "Failed to create a temp file name.");
289
290 // Try to open the newly created empty file (remember, GetTempFileName() is kind enough to create a file for us)
291 // with a handle to automatically delete the file on close. Ignore any failure that might happen, since the worst
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);
294
295 hr = DictCreateWithEmbeddedKey(&pcd->shDictHandle, dwMaxFiles, reinterpret_cast<void **>(&pcd->prgFiles), offsetof(CABC_FILE, pwzSourcePath), DICT_FLAG_CASEINSENSITIVE);
296 CabcExitOnFailure(hr, "Failed to create dictionary to keep track of duplicate files");
@@ -358,6 +332,8 @@ extern "C" HRESULT DAPI CabCBegin(
332 *phContext = pcd;
333
334 LExit:
335 + ReleaseStr(pwzPathBuffer);
336 +
337 if (FAILED(hr) && pcd && pcd->hfci)
338 {
339 ::FCIDestroy(pcd->hfci);
@@ -527,7 +503,7 @@ extern "C" HRESULT DAPI CabCFinish(
503 // files point at the same path (the empty file) so there is no point in tracking them with
504 // their path.
505 fileInfo.wzSourcePath = pcd->prgDuplicates[dwDupeArrayFileIndex].pwzSourcePath;
530 - fileInfo.wzEmptyPath = pcd->wzEmptyFile;
506 + fileInfo.wzEmptyPath = pcd->sczEmptyFile;
507
508 // Use the provided token, otherwise default to the source file name.
509 if (pcd->prgDuplicates[dwDupeArrayFileIndex].pwzToken)
@@ -643,7 +619,7 @@ extern "C" HRESULT DAPI CabCFinish(
619 if (pcd->fGoodCab && pcd->cDuplicates)
620 {
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);
623 }
624
625 LExit:
@@ -692,6 +668,9 @@ static void FreeCabCData(
668 ReleaseMem(pcd->prgFiles);
669 ReleaseMem(pcd->prgDuplicates);
670
671 + ReleaseStr(pcd->sczCabinetPath);
672 + ReleaseStr(pcd->sczEmptyFile);
673 +
674 ReleaseMem(pcd);
675 }
676 }
@@ -709,7 +688,7 @@ static HRESULT CheckForDuplicateFile(
688 __in LONGLONG llFileSize
689 )
690 {
712 - DWORD i;
691 + DWORD i = 0;
692 HRESULT hr = S_OK;
693 UINT er = ERROR_SUCCESS;
694
@@ -916,17 +895,17 @@ static HRESULT UpdateDuplicateFiles(
895 LPVOID pv = NULL;
896 MS_CABINET_HEADER *pCabinetHeader = NULL;
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);
899 if (INVALID_HANDLE_VALUE == hCabinet)
900 {
922 - CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->wzCabinetPath);
901 + CabcExitWithLastError(hr, "Failed to open cabinet: %ls", pcd->sczCabinetPath);
902 }
903
904 // Shouldn't need more than 16 MB to get the whole cabinet header into memory so use that as
905 // the upper bound for the memory map.
906 if (!::GetFileSizeEx(hCabinet, &liCabinetSize))
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);
909 }
910
911 if (0 == liCabinetSize.HighPart && liCabinetSize.LowPart < MAX_CABINET_HEADER_SIZE)
@@ -942,11 +921,11 @@ static HRESULT UpdateDuplicateFiles(
921 hCabinetMapping = ::CreateFileMappingW(hCabinet, NULL, PAGE_READWRITE | SEC_COMMIT, 0, cbCabinet, NULL);
922 if (NULL == hCabinetMapping || INVALID_HANDLE_VALUE == hCabinetMapping)
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);
925 }
926
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);
929
930 pCabinetHeader = static_cast<MS_CABINET_HEADER*>(pv);
931
@@ -1155,7 +1134,7 @@ static __callback INT_PTR DIAMONDAPI CabCOpen(
1134
1135 if (INVALID_HANDLE_VALUE == reinterpret_cast<HANDLE>(pFile))
1136 {
1158 - CabcExitOnLastError(hr, "failed to open file: %s", pszFile);
1137 + CabcExitOnLastError(hr, "failed to open file: %hs", pszFile);
1138 }
1139
1140 LExit:
@@ -1326,11 +1305,12 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile(
1305
1306 HRESULT hr = S_OK;
1307 char szTempPath[MAX_PATH] = { };
1329 - DWORD cchTempPath = MAX_PATH;
1308 DWORD dwProcessId = ::GetCurrentProcessId();
1309 HANDLE hTempFile = INVALID_HANDLE_VALUE;
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))
1314 {
1315 CabcExitWithLastError(hr, "Failed to get temp path during cabinet creation.");
1316 }
@@ -1339,7 +1319,7 @@ static __callback BOOL DIAMONDAPI CabCGetTempFile(
1319 {
1320 LONG dwTempIndex = ::InterlockedIncrement(reinterpret_cast<volatile LONG*>(&dwIndex));
1321
1342 - hr = ::StringCbPrintfA(szFile, cbFile, "%s\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1322 + hr = ::StringCbPrintfA(szFile, cbFile, "%hs\\%08x.%03x", szTempPath, dwTempIndex, dwProcessId);
1323 CabcExitOnFailure(hr, "failed to format log file path.");
1324
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(
1331 }
1332 else
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 + }
1339 }
1340 }
1341 CabcExitOnFailure(hr, "failed to find temporary file.");
@@ -1386,7 +1370,7 @@ static __callback BOOL DIAMONDAPI CabCGetNextCabinet(
1370 if (pccab->iCab == 1)
1371 {
1372 pcd->wzFirstCabinetName[0] = '\0';
1389 - LPCWSTR pwzCabinetName = PathFile(pcd->wzCabinetPath);
1373 + LPCWSTR pwzCabinetName = PathFile(pcd->sczCabinetPath);
1374 size_t len = wcsnlen(pwzCabinetName, sizeof(pwzCabinetName));
1375 if (len > 4)
1376 {
src/libs/dutil/WixToolset.DUtil/cabutil.cpp
+33 -35
@@ -261,15 +261,15 @@ static HRESULT DAPI CabOperation(
261 )
262 {
263 HRESULT hr = S_OK;
264 - BOOL fResult;
264 + BOOL fResult = FALSE;
265
266 LPWSTR sczCabinet = NULL;
267 LPWSTR pwz = NULL;
268 - CHAR szCabDirectory[MAX_PATH * 4]; // Make sure these are big enough for UTF-8 strings
269 - CHAR szCabFile[MAX_PATH * 4];
268 + LPSTR pszCabDirectory = NULL;
269 + CHAR szCabFile[MAX_PATH * 4] = { }; // Make sure this is big enough for UTF-8 strings
270
271 - CAB_CALLBACK_STRUCT ccs;
272 - PFNFDINOTIFY pfnFdiNotify;
271 + CAB_CALLBACK_STRUCT ccs = { };
272 + PFNFDINOTIFY pfnFdiNotify = NULL;
273
274 //
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.
300 if (wzCabinet == pwz)
301 {
302 - hr = ::StringCchCopyA(szCabDirectory, countof(szCabDirectory), ".\\");
302 + hr = StrAnsiAllocStringAnsi(&pszCabDirectory, ".\\", 0);
303 CabExitOnFailure(hr, "Failed to copy relative current directory as cabinet directory.");
304 }
305 else
306 {
307 - if (!::WideCharToMultiByte(CP_UTF8, 0, sczCabinet, -1, szCabDirectory, countof(szCabDirectory), NULL, NULL))
308 - {
309 - CabExitWithLastError(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
310 - }
307 + hr = StrAnsiAllocString(&pszCabDirectory, sczCabinet, 0, CP_UTF8);
308 + CabExitOnFailure(hr, "failed to convert cabinet directory to ASCII: %ls", sczCabinet);
309 }
310
311 //
@@ -331,7 +329,7 @@ static HRESULT DAPI CabOperation(
329 v_pfnNetFx11Notify = pfnNotify;
330 pfnFdiNotify = FDINotify;
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));
333 if (!fResult && !ccs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
334 {
335 CabExitWithLastError(hr, "failed to extract cabinet file: %ls", sczCabinet);
@@ -339,6 +337,7 @@ static HRESULT DAPI CabOperation(
337
338 LExit:
339 ReleaseStr(sczCabinet);
340 + ReleaseStr(pszCabDirectory);
341 v_pfnNetFx11Notify = NULL;
342
343 return hr;
@@ -493,14 +492,15 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
492 INT_PTR ipResult = 0; // result to return on success
493
494 CAB_CALLBACK_STRUCT* pccs = static_cast<CAB_CALLBACK_STRUCT*>(pFDINotify->pv);
496 - LPCSTR sz;
497 - WCHAR wz[MAX_PATH];
498 - FILETIME ft;
495 + LPCSTR sz = NULL;
496 + LPWSTR pwz = NULL;
497 + LPWSTR pwzPath = NULL;
498 + FILETIME ft = { };
499
500 switch (iNotification)
501 {
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");
505
506 if (pccs->fStopExtracting)
@@ -510,40 +510,37 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
510
511 // convert params to useful variables
512 sz = static_cast<LPCSTR>(pFDINotify->psz1);
513 - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
514 - {
515 - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
516 - }
513 + CabExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
514 +
515 + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
516 + CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
517
518 if (pccs->pfnProgress)
519 {
520 - hr = pccs->pfnProgress(TRUE, wz, pccs->pvContext);
520 + hr = pccs->pfnProgress(TRUE, pwz, pccs->pvContext);
521 if (S_OK != hr)
522 {
523 ExitFunction();
524 }
525 }
526
527 - if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, wz))
527 + if (L'*' == *pccs->pwzExtract || 0 == lstrcmpW(pccs->pwzExtract, pwz))
528 {
529 // get the created date for the resource in the cabinet
530 FILETIME ftLocal;
531 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ftLocal))
532 {
533 - CabExitWithLastError(hr, "failed to get time for resource: %ls", wz);
533 + CabExitWithLastError(hr, "failed to get time for resource: %ls", pwz);
534 }
535 ::LocalFileTimeToFileTime(&ftLocal, &ft);
536
537 - WCHAR wzPath[MAX_PATH];
538 - hr = ::StringCchCopyW(wzPath, countof(wzPath), pccs->pwzExtractDir);
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);
537 + hr = PathConcat(pccs->pwzExtractDir, pwz, &pwzPath);
538 + CabExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", pccs->pwzExtractDir, pwz);
539
543 - hFile = OpenFileWithRetry(wzPath, GENERIC_WRITE, CREATE_ALWAYS);
540 + hFile = OpenFileWithRetry(pwzPath, GENERIC_WRITE, CREATE_ALWAYS);
541 if (INVALID_HANDLE_VALUE == hFile)
542 {
546 - CabExitWithLastError(hr, "failed to create file: %ls", wzPath);
543 + CabExitWithLastError(hr, "failed to create file: %ls", pwzPath);
544 }
545
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
564
565 break;
566 case fdintCLOSE_FILE_INFO: // resource extraction complete
570 - Assert(pFDINotify->hf && pFDINotify->psz1);
567 + Assert(pFDINotify->hf && pccs && pFDINotify->psz1);
568 CabExitOnNull(pccs, hr, E_INVALIDARG, "Failed to call cabextract callback, because no callback struct was provided");
569
570 // convert params to useful variables
571 sz = static_cast<LPCSTR>(pFDINotify->psz1);
572 CabExitOnNull(sz, hr, E_INVALIDARG, "Failed to convert cabinet file id, because no cabinet file id was provided");
573
577 - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
578 - {
579 - CabExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
580 - }
574 + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
575 + CabExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
576
577 if (NULL != pFDINotify->hf) // just close the file
578 {
@@ -586,7 +581,7 @@ static __callback INT_PTR DIAMONDAPI CabExtractCallback(__in FDINOTIFICATIONTYPE
581
582 if (pccs->pfnProgress)
583 {
589 - hr = pccs->pfnProgress(FALSE, wz, pccs->pvContext);
584 + hr = pccs->pfnProgress(FALSE, pwz, pccs->pvContext);
585 }
586
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
608 LExit:
609 ReleaseFileHandle(hFile);
610
611 + ReleaseStr(pwz);
612 + ReleaseStr(pwzPath);
613 +
614 return (S_OK == hr) ? ipResult : -1;
615 }
src/libs/dutil/WixToolset.DUtil/dirutil.cpp
+20 -44
@@ -59,34 +59,10 @@ LExit:
59 *******************************************************************/
60 extern "C" HRESULT DAPI DirCreateTempPath(
61 __in_z LPCWSTR wzPrefix,
62 - __out_ecount_z(cchPath) LPWSTR wzPath,
63 - __in DWORD cchPath
62 + __out_opt LPWSTR* psczTempFile
63 )
64 {
66 - Assert(wzPrefix);
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 -
88 -LExit:
89 - return hr;
65 + return PathCreateTempFile(NULL, NULL, 0, wzPrefix, 0, psczTempFile, NULL);
66 }
67
68
@@ -192,18 +168,19 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
168 Assert(wzPath && *wzPath);
169
170 HRESULT hr = S_OK;
195 - DWORD er;
171 + DWORD er = ERROR_SUCCESS;
172
197 - DWORD dwAttrib;
173 + DWORD dwAttrib = 0;
174 HANDLE hFind = INVALID_HANDLE_VALUE;
175 LPWSTR sczDelete = NULL;
200 - WIN32_FIND_DATAW wfd;
176 + WIN32_FIND_DATAW wfd = { };
177
178 BOOL fDeleteFiles = (DIR_DELETE_FILES == (dwFlags & DIR_DELETE_FILES));
179 BOOL fRecurse = (DIR_DELETE_RECURSE == (dwFlags & DIR_DELETE_RECURSE));
180 BOOL fScheduleDelete = (DIR_DELETE_SCHEDULE == (dwFlags & DIR_DELETE_SCHEDULE));
205 - WCHAR wzTempDirectory[MAX_PATH] = { };
206 - WCHAR wzTempPath[MAX_PATH] = { };
181 + WCHAR wzSafeFileName[MAX_PATH + 1] = { };
182 + LPWSTR sczTempDirectory = NULL;
183 + LPWSTR sczTempPath = NULL;
184
185 if (-1 == (dwAttrib = ::GetFileAttributesW(wzPath)))
186 {
@@ -231,10 +208,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
208 {
209 if (fScheduleDelete)
210 {
234 - if (!::GetTempPathW(countof(wzTempDirectory), wzTempDirectory))
235 - {
236 - DirExitWithLastError(hr, "Failed to get temp directory.");
237 - }
211 + hr = PathGetTempPath(&sczTempDirectory, NULL);
212 + DirExitOnFailure(hr, "Failed to get temp directory.");
213 }
214
215 // Delete everything in this directory.
@@ -256,10 +231,11 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
231 }
232
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.");
236
261 - hr = PathConcat(wzPath, wfd.cFileName, &sczDelete);
262 - DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wfd.cFileName, wzPath);
237 + hr = PathConcat(wzPath, wzSafeFileName, &sczDelete);
238 + DirExitOnFailure(hr, "Failed to concat filename '%ls' to directory: %ls", wzSafeFileName, wzPath);
239
240 if (fRecurse && wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
241 {
@@ -288,16 +264,14 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
264 {
265 if (fScheduleDelete)
266 {
291 - if (!::GetTempFileNameW(wzTempDirectory, L"DEL", 0, wzTempPath))
292 - {
293 - DirExitWithLastError(hr, "Failed to get temp file to move to.");
294 - }
267 + hr = PathGetTempFileName(sczTempDirectory, L"DEL", 0, &sczTempPath);
268 + DirExitOnFailure(hr, "Failed to get temp file to move to.");
269
270 // Try to move the file to the temp directory then 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))
273 {
300 - ::MoveFileExW(wzTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
274 + ::MoveFileExW(sczTempPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
275 }
276 else
277 {
@@ -348,6 +322,8 @@ extern "C" HRESULT DAPI DirEnsureDeleteEx(
322 LExit:
323 ReleaseFileFindHandle(hFind);
324 ReleaseStr(sczDelete);
325 + ReleaseStr(sczTempDirectory);
326 + ReleaseStr(sczTempPath);
327
328 return hr;
329 }
src/libs/dutil/WixToolset.DUtil/fileutil.cpp
+5 -7
@@ -1284,21 +1284,18 @@ extern "C" HRESULT DAPI FileCreateTempW(
1284 Assert(wzPrefix && *wzPrefix);
1285 HRESULT hr = E_FAIL;
1286
1287 - WCHAR wzTempPath[MAX_PATH];
1288 - DWORD cchTempPath = countof(wzTempPath);
1287 + LPWSTR pwzTempPath = NULL;
1288 LPWSTR pwzTempFile = NULL;
1289
1290 HANDLE hTempFile = INVALID_HANDLE_VALUE;
1291 int i = 0;
1292
1294 - if (!::GetTempPathW(cchTempPath, wzTempPath))
1295 - {
1296 - FileExitOnLastError(hr, "failed to get temp path");
1297 - }
1293 + hr = PathGetTempPath(&pwzTempPath, NULL);
1294 + FileExitOnFailure(hr, "failed to get temp path");
1295
1296 for (i = 0; i < 1000 && INVALID_HANDLE_VALUE == hTempFile; ++i)
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);
1299 FileExitOnFailure(hr, "failed to allocate memory for temp filename");
1300
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(
1327 LExit:
1328 ReleaseFile(hTempFile);
1329 ReleaseStr(pwzTempFile);
1330 + ReleaseStr(pwzTempPath);
1331
1332 return hr;
1333 }
src/libs/dutil/WixToolset.DUtil/inc/dirutil.h
+1 -2
@@ -20,8 +20,7 @@ BOOL DAPI DirExists(
20
21 HRESULT DAPI DirCreateTempPath(
22 __in_z LPCWSTR wzPrefix,
23 - __out_ecount_z(cchPath) LPWSTR wzPath,
24 - __in DWORD cchPath
23 + __out_opt LPWSTR* psczTempFile
24 );
25
26 HRESULT DAPI DirEnsureExists(
src/libs/dutil/WixToolset.DUtil/inc/pathutil.h
+59 -8
@@ -159,18 +159,30 @@ DAPI_(HRESULT) PathRelativeToModule(
159 /*******************************************************************
160 PathCreateTempFile
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.
164 *******************************************************************/
165 DAPI_(HRESULT) PathCreateTempFile(
166 __in_opt LPCWSTR wzDirectory,
167 __in_opt __format_string LPCWSTR wzFileNameTemplate,
168 __in DWORD dwUniqueCount,
169 + __in_z LPCWSTR wzPrefix,
170 __in DWORD dwFileAttributes,
171 __out_opt LPWSTR* psczTempFile,
172 __out_opt HANDLE* phTempFile
173 );
174
175 +/*******************************************************************
176 + PathGetTempFileName - wrapper around ::GetTempFileName.
177 + If the wzPathName is too long, it will use its own algorithm.
178 +*******************************************************************/
179 +DAPI_(HRESULT) PathGetTempFileName(
180 + __in LPCWSTR wzPathName,
181 + __in LPCWSTR wzPrefixString,
182 + __in UINT uUnique,
183 + __out LPWSTR* psczTempFileName
184 + );
185 +
186 /*******************************************************************
187 PathCreateTimeBasedTempFile - creates an empty temp file based on current
188 system time
@@ -187,7 +199,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
199 /*******************************************************************
200 PathCreateTempDirectory
201
190 - Note: if wzDirectory is null, ::GetTempPath() will be used instead.
202 + Note: if wzDirectory is null, ::GetTempPath2() will be used instead.
203 *******************************************************************/
204 DAPI_(HRESULT) PathCreateTempDirectory(
205 __in_opt LPCWSTR wzDirectory,
@@ -201,7 +213,24 @@ DAPI_(HRESULT) PathCreateTempDirectory(
213 that is backslash terminated.
214 *******************************************************************/
215 DAPI_(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 +*******************************************************************/
224 +DAPI_(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 +*******************************************************************/
232 +DAPI_(HRESULT) PathGetSystemWow64Directory(
233 + __out_z LPWSTR* psczSystemPath
234 );
235
236 /*******************************************************************
@@ -223,12 +252,11 @@ DAPI_(HRESULT) PathGetSystemTempPaths(
252 );
253
254 /*******************************************************************
226 - PathGetKnownFolder - returns the path to a well-known shell folder
227 -
255 + PathGetVolumePathName - wrapper for ::GetVolumePathNameW.
256 *******************************************************************/
229 -DAPI_(HRESULT) PathGetKnownFolder(
230 - __in int csidl,
231 - __out LPWSTR* psczKnownFolder
257 +DAPI_(HRESULT) PathGetVolumePathName(
258 + __in_z LPCWSTR wzFileName,
259 + __out_z LPWSTR* psczVolumePathName
260 );
261
262 /*******************************************************************
@@ -340,6 +368,20 @@ DAPI_(HRESULT) PathGetHierarchyArray(
368 __inout LPUINT pcPathArray
369 );
370
371 +/********************************************************************
372 + Path2FunctionAllowFallback - allow functions only available in newer versions of Windows.
373 + Typically used for unit testing.
374 +
375 +*********************************************************************/
376 +void DAPI Path2FunctionAllowFallback();
377 +
378 +/********************************************************************
379 + Path2FunctionForceFallback - ignore functions only available in newer versions of Windows.
380 + Typically used for unit testing.
381 +
382 +*********************************************************************/
383 +void DAPI Path2FunctionForceFallback();
384 +
385 /*******************************************************************
386 PathCanonicalizePath - wrapper around PathCanonicalizeW.
387 *******************************************************************/
@@ -348,6 +390,15 @@ DAPI_(HRESULT) PathCanonicalizePath(
390 __deref_out_z LPWSTR* psczCanonicalized
391 );
392
393 +/*******************************************************************
394 + PathAllocCanonicalizePath - wrapper around PathAllocCanonicalize.
395 +*******************************************************************/
396 +DAPI_(HRESULT) PathAllocCanonicalizePath(
397 + __in_z LPCWSTR wzPath,
398 + __in DWORD dwFlags,
399 + __deref_out_z LPWSTR* psczCanonicalized
400 + );
401 +
402 /*******************************************************************
403 PathCanonicalizeForComparison - canonicalizes the path based on the given flags.
404 . and .. directories are collapsed.
src/libs/dutil/WixToolset.DUtil/inc/shelutil.h
+15
@@ -33,10 +33,25 @@ HRESULT DAPI ShelExecUnelevated(
33 __in_z_opt LPCWSTR wzWorkingDirectory,
34 __in int nShowCmd
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 +*******************************************************************/
44 HRESULT DAPI ShelGetFolder(
45 __out_z LPWSTR* psczFolderPath,
46 __in int csidlFolder
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 +*******************************************************************/
55 HRESULT DAPI ShelGetKnownFolder(
56 __out_z LPWSTR* psczFolderPath,
57 __in REFKNOWNFOLDERID rfidFolder
src/libs/dutil/WixToolset.DUtil/logutil.cpp
+14 -5
@@ -684,9 +684,10 @@ LExit:
684 extern "C" HRESULT DAPI LogHeader()
685 {
686 HRESULT hr = S_OK;
687 - WCHAR wzComputerName[MAX_PATH];
687 + WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { };
688 DWORD cchComputerName = countof(wzComputerName);
689 - WCHAR wzPath[MAX_PATH];
689 + LPWSTR sczPath = NULL;
690 + LPCWSTR wzPath = NULL;
691 DWORD dwMajorVersion = 0;
692 DWORD dwMinorVersion = 0;
693 LPCSTR szLevel = LOGUTIL_UNKNOWN;
@@ -695,12 +696,19 @@ extern "C" HRESULT DAPI LogHeader()
696 //
697 // get the interesting data
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
706 {
700 - memset(wzPath, 0, sizeof(wzPath));
707 + wzPath = sczPath;
708 +
709 + hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
710 }
711
703 - hr = FileVersion(wzPath, &dwMajorVersion, &dwMinorVersion);
712 if (FAILED(hr))
713 {
714 dwMajorVersion = 0;
@@ -743,6 +751,7 @@ extern "C" HRESULT DAPI LogHeader()
751 hr = S_OK;
752
753 ReleaseStr(sczCurrentDateTime);
754 + ReleaseStr(sczPath);
755
756 return hr;
757 }
src/libs/dutil/WixToolset.DUtil/path2utl.cpp
+133 -12
@@ -19,6 +19,65 @@
19 #define PathExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PATHUTIL, g, x, s, __VA_ARGS__)
20
21
22 +typedef HRESULT(WINAPI* PFN_PATH_ALLOC_CANONICALIZE)(
23 + __in LPCWSTR wzSource,
24 + __in DWORD dwFlags,
25 + __out_z LPWSTR* psczPathOut
26 + );
27 +
28 +static BOOL vfInitialized = FALSE;
29 +static HMODULE vhPathApiSet_1_1_0 = NULL;
30 +static PFN_PATH_ALLOC_CANONICALIZE vpfnPathAllocCanonicalize = NULL;
31 +static BOOL vfForceFallback = FALSE;
32 +
33 +// from PathCch.h
34 +#ifndef PATHCCH_ALLOW_LONG_PATHS
35 +#define PATHCCH_ALLOW_LONG_PATHS 0x01
36 +#endif
37 +
38 +static 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 +
64 +LExit:
65 + return hr;
66 +}
67 +
68 +
69 +DAPI_(void) Path2FunctionAllowFallback()
70 +{
71 + vfForceFallback = FALSE;
72 +}
73 +
74 +
75 +DAPI_(void) Path2FunctionForceFallback()
76 +{
77 + vfForceFallback = TRUE;
78 +}
79 +
80 +
81 DAPI_(HRESULT) PathCanonicalizePath(
82 __in_z LPCWSTR wzPath,
83 __deref_out_z LPWSTR* psczCanonicalized
@@ -43,6 +102,37 @@ LExit:
102 return hr;
103 }
104
105 +DAPI_(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 +
127 +LExit:
128 + if (sczCanonicalizedPath)
129 + {
130 + ::LocalFree(sczCanonicalizedPath);
131 + }
132 +
133 + return hr;
134 +}
135 +
136 DAPI_(HRESULT) PathCanonicalizeForComparison(
137 __in_z LPCWSTR wzPath,
138 __in DWORD dwCanonicalizeFlags,
@@ -75,7 +165,19 @@ DAPI_(HRESULT) PathCanonicalizeForComparison(
165
166 if (*wzNormalizedPath)
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 + }
181 PathExitOnFailure(hr, "Failed to canonicalize: %ls", wzNormalizedPath);
182 }
183 else
@@ -273,33 +375,52 @@ DAPI_(HRESULT) PathSystemWindowsSubdirectory(
375 )
376 {
377 HRESULT hr = S_OK;
276 - WCHAR wzTempPath[MAX_PATH + 1] = { };
378 + LPWSTR sczWindowsPath = NULL;
379 + DWORD cchBuffer = MAX_PATH + 1;
380 DWORD cch = 0;
381
279 - cch = ::GetSystemWindowsDirectoryW(wzTempPath, countof(wzTempPath));
280 - if (!cch)
281 - {
282 - PathExitWithLastError(hr, "Failed to get Windows directory path.");
283 - }
284 - else if (cch >= countof(wzTempPath))
382 + hr = StrAlloc(&sczWindowsPath, cchBuffer);
383 + PathExitOnFailure(hr, "Failed to alloc Windows directory path.");
384 +
385 + cch = ::GetSystemWindowsDirectoryW(sczWindowsPath, cchBuffer);
386 + PathExitOnNullWithLastError(cch, hr, "Failed to get Windows directory path with default size.");
387 +
388 + cch += 1; // add 1 for null terminator.
389 +
390 + if (cch > cchBuffer)
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 + }
406 }
407
408 if (wzSubdirectory)
409 {
291 - hr = PathConcatRelativeToBase(wzTempPath, wzSubdirectory, psczFullPath);
410 + hr = PathConcatRelativeToBase(sczWindowsPath, wzSubdirectory, psczFullPath);
411 PathExitOnFailure(hr, "Failed to concat subdirectory on Windows directory path.");
412 }
413 else
414 {
296 - hr = StrAllocString(psczFullPath, wzTempPath, 0);
297 - PathExitOnFailure(hr, "Failed to copy Windows directory path.");
415 + *psczFullPath = sczWindowsPath;
416 + sczWindowsPath = NULL;
417 }
418
419 hr = PathBackslashTerminate(psczFullPath);
420 PathExitOnFailure(hr, "Failed to terminate Windows directory path with backslash.");
421
422 LExit:
423 + ReleaseStr(sczWindowsPath);
424 +
425 return hr;
426 }
src/libs/dutil/WixToolset.DUtil/pathutil.cpp
+340 -64
@@ -20,6 +20,11 @@
20
21 #define PATH_GOOD_ENOUGH 64
22
23 +typedef DWORD(APIENTRY* PFN_GETTEMPPATH2W)(
24 + __in DWORD BufferLength,
25 + __out LPWSTR Buffer
26 + );
27 +
28 static BOOL IsPathSeparatorChar(
29 __in WCHAR wc
30 );
@@ -527,28 +532,55 @@ DAPI_(HRESULT) PathForCurrentProcess(
532 )
533 {
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;
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())
548 {
534 - hr = StrAlloc(psczFullPath, cch);
535 - PathExitOnFailure(hr, "Failed to allocate string for module path.");
549 + hr = StrMaxLength(*psczFullPath, &cchMax);
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);
563
537 - DWORD cchRequired = ::GetModuleFileNameW(hModule, *psczFullPath, cch);
538 - if (0 == cchRequired)
564 + for (DWORD i = 0; i < dwMaxAttempts; ++i)
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())
570 {
540 - PathExitWithLastError(hr, "Failed to get path for executing process.");
571 + break;
572 }
542 - else if (cchRequired == cch)
573 +
574 + if ((dwMaxAttempts - 1) == i)
575 {
544 - cch = cchRequired + 1;
545 - hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
576 + PathExitWithRootFailure(hr, E_FAIL, "Unexpected failure getting path for executing process.");
577 }
547 - else
548 - {
549 - hr = S_OK;
550 - }
551 - } while (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) == hr);
578 +
579 + cchBuffer *= 2;
580 +
581 + hr = StrAlloc(psczFullPath, cchBuffer);
582 + PathExitOnFailure(hr, "Failed to re-allocate more space for module path.");
583 + }
584
585 LExit:
586 return hr;
@@ -582,17 +614,18 @@ DAPI_(HRESULT) PathCreateTempFile(
614 __in_opt LPCWSTR wzDirectory,
615 __in_opt __format_string LPCWSTR wzFileNameTemplate,
616 __in DWORD dwUniqueCount,
617 + __in_z LPCWSTR wzPrefix,
618 __in DWORD dwFileAttributes,
619 __out_opt LPWSTR* psczTempFile,
620 __out_opt HANDLE* phTempFile
621 )
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.");
625
626 HRESULT hr = S_OK;
627
628 LPWSTR sczTempPath = NULL;
595 - DWORD cchTempPath = MAX_PATH;
629
630 HANDLE hTempFile = INVALID_HANDLE_VALUE;
631 LPWSTR scz = NULL;
@@ -605,13 +638,8 @@ DAPI_(HRESULT) PathCreateTempFile(
638 }
639 else
640 {
608 - hr = StrAlloc(&sczTempPath, cchTempPath);
609 - PathExitOnFailure(hr, "Failed to allocate memory for the temp path.");
610 -
611 - if (!::GetTempPathW(cchTempPath, sczTempPath))
612 - {
613 - PathExitWithLastError(hr, "Failed to get temp path.");
614 - }
641 + hr = PathGetTempPath(&sczTempPath, NULL);
642 + PathExitOnFailure(hr, "Failed to get temp path.");
643 }
644
645 if (wzFileNameTemplate && *wzFileNameTemplate)
@@ -621,7 +649,7 @@ DAPI_(HRESULT) PathCreateTempFile(
649 hr = StrAllocFormatted(&scz, wzFileNameTemplate, i);
650 PathExitOnFailure(hr, "Failed to allocate memory for file template.");
651
624 - hr = StrAllocFormatted(&sczTempFile, L"%s%s", sczTempPath, scz);
652 + hr = StrAllocFormatted(&sczTempFile, L"%ls%ls", sczTempPath, scz);
653 PathExitOnFailure(hr, "Failed to allocate temp file name.");
654
655 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_NEW, dwFileAttributes, NULL);
@@ -642,13 +670,8 @@ DAPI_(HRESULT) PathCreateTempFile(
670 // the system to provide us a temp file using its built-in mechanism.
671 if (INVALID_HANDLE_VALUE == hTempFile)
672 {
645 - hr = StrAlloc(&sczTempFile, MAX_PATH);
646 - PathExitOnFailure(hr, "Failed to allocate memory for the temp path");
647 -
648 - if (!::GetTempFileNameW(sczTempPath, L"TMP", 0, sczTempFile))
649 - {
650 - PathExitWithLastError(hr, "Failed to create new temp file name.");
651 - }
673 + hr = PathGetTempFileName(sczTempPath, wzPrefix, 0, &sczTempFile);
674 + PathExitOnFailure(hr, "Failed to create new temp file name.");
675
676 hTempFile = ::CreateFileW(sczTempFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, dwFileAttributes, NULL);
677 if (INVALID_HANDLE_VALUE == hTempFile)
@@ -684,6 +707,82 @@ LExit:
707 }
708
709
710 +DAPI_(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 +
773 +LExit:
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 +
786 DAPI_(HRESULT) PathCreateTimeBasedTempFile(
787 __in_z_opt LPCWSTR wzDirectory,
788 __in_z LPCWSTR wzPrefix,
@@ -695,7 +794,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
794 {
795 HRESULT hr = S_OK;
796 BOOL fRetry = FALSE;
698 - WCHAR wzTempPath[MAX_PATH] = { };
797 + LPWSTR sczTempParentPath = NULL;
798 LPWSTR sczPrefix = NULL;
799 LPWSTR sczPrefixFolder = NULL;
800 SYSTEMTIME time = { };
@@ -711,12 +810,10 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
810 }
811 else
812 {
714 - if (!::GetTempPathW(countof(wzTempPath), wzTempPath))
715 - {
716 - PathExitWithLastError(hr, "Failed to get temp folder.");
717 - }
813 + hr = PathGetTempPath(&sczTempParentPath, NULL);
814 + PathExitOnFailure(hr, "Failed to get temp folder.");
815
719 - hr = PathConcat(wzTempPath, wzPrefix, &sczPrefix);
816 + hr = PathConcat(sczTempParentPath, wzPrefix, &sczPrefix);
817 PathExitOnFailure(hr, "Failed to concatenate the temp folder and log prefix.");
818 }
819
@@ -778,6 +875,7 @@ DAPI_(HRESULT) PathCreateTimeBasedTempFile(
875
876 LExit:
877 ReleaseFile(hTempFile);
878 + ReleaseStr(sczTempParentPath);
879 ReleaseStr(sczTempPath);
880 ReleaseStr(sczPrefixFolder);
881 ReleaseStr(sczPrefix);
@@ -799,7 +897,6 @@ DAPI_(HRESULT) PathCreateTempDirectory(
897 HRESULT hr = S_OK;
898
899 LPWSTR sczTempPath = NULL;
802 - DWORD cchTempPath = MAX_PATH;
900
901 LPWSTR scz = NULL;
902
@@ -813,13 +910,8 @@ DAPI_(HRESULT) PathCreateTempDirectory(
910 }
911 else
912 {
816 - hr = StrAlloc(&sczTempPath, cchTempPath);
817 - PathExitOnFailure(hr, "Failed to allocate memory for the temp path.");
818 -
819 - if (!::GetTempPathW(cchTempPath, sczTempPath))
820 - {
821 - PathExitWithLastError(hr, "Failed to get temp path.");
822 - }
913 + hr = PathGetTempPath(&sczTempPath, NULL);
914 + PathExitOnFailure(hr, "Failed to get temp path.");
915 }
916
917 for (DWORD i = 1; i <= dwUniqueCount; ++i)
@@ -869,46 +961,230 @@ LExit:
961
962
963 DAPI_(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 +
1030 +LExit:
1031 + return hr;
1032 +}
1033 +
1034 +
1035 +DAPI_(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 +
1081 +LExit:
1082 + return hr;
1083 +}
1084 +
1085 +
1086 +DAPI_(HRESULT) PathGetSystemWow64Directory(
1087 + __out_z LPWSTR* psczSystemPath
1088 )
1089 {
1090 HRESULT hr = S_OK;
876 - WCHAR wzTempPath[MAX_PATH + 1] = { };
1091 + SIZE_T cchMax = 0;
1092 + DWORD cchBuffer = 0;
1093 DWORD cch = 0;
1094
879 - cch = ::GetTempPathW(countof(wzTempPath), wzTempPath);
880 - if (!cch)
1095 + if (*psczSystemPath)
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);
1101 }
884 - else if (cch >= countof(wzTempPath))
1102 + else
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.");
1108 }
1109
889 - hr = StrAllocString(psczTempPath, wzTempPath, cch);
890 - PathExitOnFailure(hr, "Failed to copy TEMP directory path.");
1110 + cch = ::GetSystemWow64DirectoryW(*psczSystemPath, cchBuffer);
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.");
1135
1136 LExit:
1137 return hr;
1138 }
1139
1140
897 -DAPI_(HRESULT) PathGetKnownFolder(
898 - __in int csidl,
899 - __out LPWSTR* psczKnownFolder
1141 +DAPI_(HRESULT) PathGetVolumePathName(
1142 + __in_z LPCWSTR wzFileName,
1143 + __out_z LPWSTR* psczVolumePathName
1144 )
1145 {
1146 HRESULT hr = S_OK;
1147 + DWORD cchBuffer = 0;
1148 + SIZE_T cchMax = 0;
1149 + const DWORD dwMaxAttempts = 20;
1150
904 - hr = StrAlloc(psczKnownFolder, MAX_PATH);
905 - PathExitOnFailure(hr, "Failed to allocate memory for known folder.");
1151 + if (*psczVolumePathName)
1152 + {
1153 + hr = StrMaxLength(*psczVolumePathName, &cchMax);
1154 + PathExitOnFailure(hr, "Failed to get max length of input buffer.");
1155
907 - hr = ::SHGetFolderPathW(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, *psczKnownFolder);
908 - PathExitOnFailure(hr, "Failed to get known folder path.");
1156 + cchBuffer = (DWORD)min(DWORD_MAX, cchMax);
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 + }
1185
910 - hr = PathBackslashTerminate(psczKnownFolder);
911 - PathExitOnFailure(hr, "Failed to ensure known folder path is backslash terminated.");
1186 + hr = PathBackslashTerminate(psczVolumePathName);
1187 + PathExitOnFailure(hr, "Failed to terminate volume path name with backslash.");
1188
1189 LExit:
1190 return hr;
src/libs/dutil/WixToolset.DUtil/rexutil.cpp
+40 -37
@@ -28,7 +28,7 @@ static ERF verf;
28 static FAKE_FILE vrgffFileTable[FILETABLESIZE];
29 static DWORD vcbRes;
30 static LPCBYTE vpbRes;
31 -static CHAR vszResource[MAX_PATH];
31 +static LPSTR vpszResource = NULL;
32 static REX_CALLBACK_WRITE vpfnWrite = NULL;
33
34 static HRESULT vhrLastError = S_OK;
@@ -85,6 +85,8 @@ LExit:
85 {
86 ::FDIDestroy(vhfdi);
87 vhfdi = NULL;
88 +
89 + ReleaseNullStr(vpszResource);
90 }
91
92 return hr;
@@ -101,6 +103,8 @@ extern "C" void RexUninitialize()
103 {
104 ::FDIDestroy(vhfdi);
105 vhfdi = NULL;
106 +
107 + ReleaseNullStr(vpszResource);
108 }
109 }
110
@@ -124,12 +128,12 @@ extern "C" HRESULT RexExtract(
128 {
129 Assert(vhfdi);
130 HRESULT hr = S_OK;
127 - BOOL fResult;
131 + BOOL fResult = FALSE;
132
133 HRSRC hResInfo = NULL;
134 HANDLE hRes = NULL;
135
132 - REX_CALLBACK_STRUCT rcs;
136 + REX_CALLBACK_STRUCT rcs = { };
137
138 // remember the write callback
139 vpfnWrite = pfnWrite;
@@ -158,7 +162,7 @@ extern "C" HRESULT RexExtract(
162 // RexExitOnLastError(hr, "failed to convert cabinet resource name to ASCII: %ls", wzResource);
163 //}
164
161 - hr = ::StringCchCopyA(vszResource, countof(vszResource), szResource);
165 + hr = StrAnsiAllocStringAnsi(&vpszResource, szResource, 0);
166 RexExitOnFailure(hr, "Failed to copy resource name to global.");
167
168 //
@@ -171,7 +175,7 @@ extern "C" HRESULT RexExtract(
175 rcs.pfnProgress = pfnProgress;
176 rcs.pvContext = pvContext;
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));
179 if (!fResult && !rcs.fStopExtracting) // if something went wrong and it wasn't us just stopping the extraction, then return a failure
180 {
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
231 RexExitOnFailure(hr, "File table exceeded");
232 }
233
230 - if (0 == lstrcmpA(vszResource, pszFile))
234 + if (0 == lstrcmpA(vpszResource, pszFile))
235 {
236 vrgffFileTable[i].fUsed = TRUE;
237 vrgffFileTable[i].fftType = MEMORY_FILE;
@@ -436,15 +440,16 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
440 HANDLE hFile = INVALID_HANDLE_VALUE;
441
442 REX_CALLBACK_STRUCT* prcs = static_cast<REX_CALLBACK_STRUCT*>(pFDINotify->pv);
439 - LPCSTR sz;
440 - WCHAR wz[MAX_PATH];
441 - FILETIME ft;
443 + LPCSTR sz = NULL;
444 + LPWSTR pwz = NULL;
445 + LPWSTR pwzPath = NULL;
446 + FILETIME ft = { };
447 int i = 0;
448
449 switch (iNotification)
450 {
451 case fdintCOPY_FILE: // beGIN extracting a resource from cabinet
447 - Assert(pFDINotify->psz1);
452 + Assert(pFDINotify->psz1 && prcs);
453
454 if (prcs->fStopExtracting)
455 {
@@ -453,55 +458,50 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
458
459 // convert params to useful variables
460 sz = static_cast<LPCSTR>(pFDINotify->psz1);
456 - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
457 - {
458 - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
459 - }
461 + RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
462 +
463 + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
464 + RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
465
466 if (prcs->pfnProgress)
467 {
463 - hr = prcs->pfnProgress(TRUE, wz, prcs->pvContext);
468 + hr = prcs->pfnProgress(TRUE, pwz, prcs->pvContext);
469 if (S_OK != hr)
470 {
471 ExitFunction();
472 }
473 }
474
470 - if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, wz))
475 + if (L'*' == *prcs->pwzExtract || 0 == lstrcmpW(prcs->pwzExtract, pwz))
476 {
477 // get the created date for the resource in the cabinet
478 if (!::DosDateTimeToFileTime(pFDINotify->date, pFDINotify->time, &ft))
479 {
475 - RexExitWithLastError(hr, "failed to get time for resource: %ls", wz);
480 + RexExitWithLastError(hr, "failed to get time for resource: %ls", pwz);
481 }
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)
484 {
485 - hr = ::StringCchCatW(wzPath, countof(wzPath), wz);
486 - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, wz);
485 + hr = PathConcat(prcs->pwzExtractDir, pwz, &pwzPath);
486 + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, pwz);
487 }
488 else
489 {
490 Assert(*prcs->pwzExtractName);
491
492 - hr = ::StringCchCatW(wzPath, countof(wzPath), prcs->pwzExtractName);
493 - RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", wzPath, prcs->pwzExtractName);
492 + hr = PathConcat(prcs->pwzExtractDir, prcs->pwzExtractName, &pwzPath);
493 + RexExitOnFailure(hr, "failed to concat onto path: %ls file: %ls", prcs->pwzExtractDir, prcs->pwzExtractName);
494 }
495
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
498 // back over the null terminator).
499 - LPWSTR wzFile = PathFile(wzPath);
499 + LPWSTR wzFile = PathFile(pwzPath);
500 WCHAR wzFileFirstChar = *wzFile;
501 *wzFile = L'\0';
502
503 - hr = DirEnsureExists(wzPath, NULL);
504 - RexExitOnFailure(hr, "failed to ensure directory: %ls", wzPath);
503 + hr = DirEnsureExists(pwzPath, NULL);
504 + RexExitOnFailure(hr, "failed to ensure directory: %ls", pwzPath);
505
506 hr = S_OK;
507
@@ -524,10 +524,10 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
524 }
525
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)
529 {
530 - RexExitWithLastError(hr, "failed to open file: %ls", wzPath);
530 + RexExitWithLastError(hr, "failed to open file: %ls", pwzPath);
531 }
532
533 vrgffFileTable[i].fUsed = TRUE;
@@ -554,20 +554,20 @@ static __callback INT_PTR DIAMONDAPI RexCallback(FDINOTIFICATIONTYPE iNotificati
554
555 break;
556 case fdintCLOSE_FILE_INFO: // resource extraction complete
557 - Assert(pFDINotify->hf && pFDINotify->psz1);
557 + Assert(pFDINotify->hf && prcs && pFDINotify->psz1);
558
559 // convert params to useful variables
560 sz = static_cast<LPCSTR>(pFDINotify->psz1);
561 - if (!::MultiByteToWideChar(CP_ACP, 0, sz, -1, wz, countof(wz)))
562 - {
563 - RexExitWithLastError(hr, "failed to convert cabinet file id to unicode: %s", sz);
564 - }
561 + RexExitOnNull(sz, hr, E_INVALIDARG, "No cabinet file ID given to convert");
562 +
563 + hr = StrAllocStringAnsi(&pwz, sz, 0, CP_ACP);
564 + RexExitOnFailure(hr, "failed to convert cabinet file id to unicode: %hs", sz);
565
566 RexClose(pFDINotify->hf);
567
568 if (prcs->pfnProgress)
569 {
570 - hr = prcs->pfnProgress(FALSE, wz, prcs->pvContext);
570 + hr = prcs->pfnProgress(FALSE, pwz, prcs->pvContext);
571 }
572
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;
598 }
599
600 + ReleaseStr(pwz);
601 + ReleaseStr(pwzPath);
602 +
603 return (S_OK == hr) ? ipResult : -1;
604 }
src/libs/dutil/WixToolset.DUtil/sceutil.cpp
+6 -5
@@ -317,7 +317,7 @@ extern "C" HRESULT DAPI SceOpenDatabase(
317 {
318 HRESULT hr = S_OK;
319 DWORD dwVersionFound = 0;
320 - WCHAR wzTempDbFile[MAX_PATH];
320 + LPWSTR sczTempDbFile = NULL;
321 LPCWSTR wzPathToOpen = NULL;
322 LPWSTR sczSchemaType = 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.
344 if (fReadOnly)
345 {
346 - hr = DirCreateTempPath(PathFile(sczFile), (LPWSTR)wzTempDbFile, _countof(wzTempDbFile));
346 + hr = DirCreateTempPath(PathFile(sczFile), &sczTempDbFile);
347 ExitOnFailure(hr, "Failed to get temp path");
348
349 - hr = FileEnsureCopy(sczFile, (LPCWSTR)wzTempDbFile, TRUE);
349 + hr = FileEnsureCopy(sczFile, sczTempDbFile, TRUE);
350 ExitOnFailure(hr, "Failed to copy file to temp path");
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");
354
355 - wzPathToOpen = (LPCWSTR)wzTempDbFile;
355 + wzPathToOpen = sczTempDbFile;
356 }
357 else
358 {
@@ -424,6 +424,7 @@ extern "C" HRESULT DAPI SceOpenDatabase(
424 LExit:
425 ReleaseBSTR(rgdbpDataSourceProp[0].vValue.bstrVal);
426 ReleaseStr(sczSchemaType);
427 + ReleaseStr(sczTempDbFile);
428 ReleaseDatabase(pNewSceDatabase);
429
430 return hr;
src/libs/dutil/WixToolset.DUtil/shelutil.cpp
+190 -20
@@ -9,6 +9,7 @@
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__)
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__)
13 #define ShelExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_SHELUTIL, x, s, __VA_ARGS__)
14 #define ShelExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_SHELUTIL, p, x, e, s, __VA_ARGS__)
15 #define ShelExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_SHELUTIL, p, x, s, __VA_ARGS__)
@@ -19,6 +20,10 @@
20
21 static PFN_SHELLEXECUTEEXW vpfnShellExecuteExW = ::ShellExecuteExW;
22
23 +static HRESULT DAPI GetFolderFromCsidl(
24 + __out_z LPWSTR* psczFolderPath,
25 + __in int csidlFolder
26 + );
27 static HRESULT GetDesktopShellView(
28 __in REFIID riid,
29 __out void **ppv
@@ -57,7 +62,14 @@ extern "C" HRESULT DAPI ShelExec(
62 )
63 {
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 + }
73
74 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
75 shExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
@@ -159,11 +171,7 @@ LExit:
171 }
172
173
162 -/********************************************************************
163 - ShelGetFolder() - gets a folder by CSIDL.
164 -
165 -*******************************************************************/
166 -extern "C" HRESULT DAPI ShelGetFolder(
174 +static HRESULT DAPI GetFolderFromCsidl(
175 __out_z LPWSTR* psczFolderPath,
176 __in int csidlFolder
177 )
@@ -185,19 +193,6 @@ LExit:
193 }
194
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 -
196 EXTERN_C typedef HRESULT (STDAPICALLTYPE *PFN_SHGetKnownFolderPath)(
197 REFKNOWNFOLDERID rfid,
198 DWORD dwFlags,
@@ -249,6 +244,181 @@ LExit:
244 return hr;
245 }
246
247 +extern "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 +
416 +LExit:
417 + ReleaseStr(sczPath);
418 +
419 + return hr;
420 +}
421 +
422
423 // Internal functions.
424
@@ -287,7 +457,7 @@ static HRESULT GetDesktopShellView(
457 else if (S_FALSE == hr)
458 {
459 //Windows XP
290 - hr = SHGetDesktopFolder(&psf);
460 + hr = ::SHGetDesktopFolder(&psf);
461 ShelExitOnFailure(hr, "Failed to get desktop folder.");
462
463 hr = psf->CreateViewObject(NULL, IID_IShellView, ppv);
src/libs/dutil/test/DUtilUnitTest/PathUtilTest.cpp
+180 -2
@@ -7,6 +7,11 @@ using namespace System::IO;
7 using namespace Xunit;
8 using namespace WixBuildTools::TestSupport;
9
10 +// from PathCch.h
11 +#ifndef PATHCCH_ALLOW_LONG_PATHS
12 +#define PATHCCH_ALLOW_LONG_PATHS 0x01
13 +#endif
14 +
15 namespace DutilTests
16 {
17 public ref class PathUtil
@@ -101,8 +106,28 @@ namespace DutilTests
106 }
107 }
108
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]
125 void PathCanonicalizeForComparisonTest()
126 + {
127 + PathCanonicalizeForComparisonTestCore(TRUE);
128 + }
129 +
130 + void PathCanonicalizeForComparisonTestCore(BOOL fLongPathSupported)
131 {
132 HRESULT hr = S_OK;
133 LPWSTR sczCanonicalized = NULL;
@@ -110,10 +135,26 @@ namespace DutilTests
135 try
136 {
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 + }
147
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 + }
158
159 hr = PathCanonicalizeForComparison(L"\\\\server", PATH_CANONICALIZE_KEEP_UNC_ROOT, &sczCanonicalized);
160 NativeAssert::Succeeded(hr, "Failed to canonicalize path");
@@ -237,6 +278,52 @@ namespace DutilTests
278 }
279 }
280
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]
328 void PathConcatTest()
329 {
@@ -544,6 +631,25 @@ namespace DutilTests
631 }
632 }
633
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]
654 void PathGetDirectoryTest()
655 {
@@ -827,6 +933,78 @@ namespace DutilTests
933 }
934 }
935
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]
1009 void PathNormalizeSlashesFixedTest()
1010 {
src/test/burn/Directory.wixproj.targets
+1
@@ -7,6 +7,7 @@
7 <WebServerBaseUrl Condition=" '$(WebServerBaseUrl)' == '' ">http://localhost:9999/e2e/</WebServerBaseUrl>
8 <DefineConstants>TestGroupName=$(TestGroupName);PackageName=$(PackageName);BundleName=$(BundleName);WebServerBaseUrl=$(WebServerBaseUrl);$(DefineConstants)</DefineConstants>
9 <DefineConstants Condition=" '$(BA)' != '' ">BA=$(BA);$(DefineConstants)</DefineConstants>
10 + <DefineConstants Condition=" '$(BundleLogDirectory)' != '' ">BundleLogDirectory=$(BundleLogDirectory);$(DefineConstants)</DefineConstants>
11 <DefineConstants Condition=" '$(CabPrefix)' != '' ">CabPrefix=$(CabPrefix);$(DefineConstants)</DefineConstants>
12 <DefineConstants Condition=" '$(IncludeSoftwareTag)' == 'true' ">SoftwareTag=1;$(DefineConstants)</DefineConstants>
13 <DefineConstants Condition=" '$(ProductCode)' != '' ">ProductCode=$(ProductCode);$(DefineConstants)</DefineConstants>
src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wixproj new
+19
@@ -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
src/test/burn/TestData/LongPathTests/NonCompressedBundle/NonCompressedBundle.wxs new
+10
@@ -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>
src/test/burn/TestData/LongPathTests/PackageA/PackageA.wixproj new
+10
@@ -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
src/test/burn/TestData/Templates/Bundle.wxs
+4 -1
@@ -3,10 +3,13 @@
3 <?ifndef Version?>
4 <?define Version = 1.0.0.0?>
5 <?endif?>
6 +<?ifndef BundleLogDirectory?>
7 +<?define BundleLogDirectory = .?>
8 +<?endif?>
9
10 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
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)" />
13
14 <Variable Name="TestGroupName" Value="$(var.TestGroupName)" />
15
src/test/burn/TestExe/Task.cs
+67
@@ -2,8 +2,10 @@
2
3 using System;
4 using System.Collections.Generic;
5 +using System.ComponentModel;
6 using System.Diagnostics;
7 using System.IO;
8 +using System.Runtime.InteropServices;
9 using Microsoft.Win32;
10
11 namespace TestExe
@@ -151,6 +153,67 @@ namespace TestExe
153 }
154 }
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 +
217 public class TaskParser
218 {
219
@@ -197,6 +260,10 @@ namespace TestExe
260 t = new FileExistsTask(args[i + 1]);
261 tasks.Add(t);
262 break;
263 + case "/dm":
264 + t = new DeleteManifestsTask(args[i + 1]);
265 + tasks.Add(t);
266 + break;
267 #if NET35
268 case "/pinfo":
269 t = new ProcessInfoTask(args[i + 1]);
src/test/burn/WixTestTools/BundleInstaller.cs
+3 -1
@@ -28,6 +28,8 @@ namespace WixTestTools
28
29 public int? AlternateExitCode { get; set; }
30
31 + public string LogDirectory { get; set; }
32 +
33 public int? LastExitCode { get; set; }
34
35 /// <summary>
@@ -194,7 +196,7 @@ namespace WixTestTools
196 sb.Append(" -quiet");
197
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));
200 sb.AppendFormat(" -log \"{0}\"", logFile);
201
202 // Set operation.
src/test/burn/WixTestTools/MSIExec.cs
+10 -4
@@ -110,7 +110,7 @@ namespace WixTestTools
110 this.NoRestart = true;
111 this.ForceRestart = false;
112 this.PromptRestart = false;
113 - this.LogFile = string.Empty;
113 + this.LogFile = String.Empty;
114 this.LoggingOptions = MSIExecLoggingOptions.VOICEWARMUP;
115 this.OtherArguments = String.Empty;
116 }
@@ -230,14 +230,14 @@ namespace WixTestTools
230 }
231
232 // logfile and logging options
233 - if (0 != loggingOptionsString.Length || !string.IsNullOrEmpty(this.LogFile))
233 + if (0 != loggingOptionsString.Length || !String.IsNullOrEmpty(this.LogFile))
234 {
235 arguments.Append(" /l");
236 if (0 != loggingOptionsString.Length)
237 {
238 arguments.AppendFormat("{0} ", loggingOptionsString);
239 }
240 - if (!string.IsNullOrEmpty(this.LogFile))
240 + if (!String.IsNullOrEmpty(this.LogFile))
241 {
242 arguments.AppendFormat(" \"{0}\" ", this.LogFile);
243 }
@@ -268,7 +268,7 @@ namespace WixTestTools
268 };
269
270 // product
271 - if (!string.IsNullOrEmpty(this.Product))
271 + if (!String.IsNullOrEmpty(this.Product))
272 {
273 arguments.AppendFormat(" \"{0}\" ", this.Product);
274 }
@@ -310,6 +310,12 @@ namespace WixTestTools
310 /// </summary>
311 ERROR_CALL_NOT_IMPLEMENTED = 120,
312
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>
320 /// ERROR_APPHELP_BLOCK 1259
321 /// If Windows Installer determines a product may be incompatible with the current operating system,
src/test/burn/WixTestTools/TestTool.cs
+1 -1
@@ -229,7 +229,7 @@ namespace WixTestTools
229 returnValue.AppendLine("Tool run result:");
230 returnValue.AppendLine("----------------");
231 returnValue.AppendLine("Command:");
232 - returnValue.AppendLine($"\"{result.StartInfo.FileName}\" {result.StartInfo.Arguments}");
232 + returnValue.AppendLine($"\"{result.FileName}\" {result.Arguments}");
233 returnValue.AppendLine();
234 returnValue.AppendLine("Standard Output:");
235 foreach (var line in result.StandardOutput ?? new string[0])
src/test/burn/WixToolsetTest.BurnE2E/LongPathTests.cs new
+298
@@ -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 +
3 +namespace 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 +}
src/test/burn/WixToolsetTest.BurnE2E/testhost.longpathaware.manifest new
+11
@@ -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>
src/test/burn/test_burn.cmd
+5
@@ -9,11 +9,16 @@
9 @if /i "%1"=="test" set RuntimeTestsEnabled=true
10 @if not "%1"=="" shift & goto parse_args
11
12 +@set _B=%~dp0..\..\..\build\IntegrationBurn\%_C%
13 +
14 @echo Burn integration tests %_C%
15
16 msbuild -t:Build -Restore -p:Configuration=%_C% -warnaserror -bl:%_L%\test_burn_build.binlog || exit /b
17 msbuild -t:Build -Restore TestData\TestData.proj -p:Configuration=%_C% -m -bl:%_L%\test_burn_data_build.binlog || exit /b
18
19 +"%_B%\net35\win-x86\testexe.exe" /dm "%_B%\netcoreapp3.1\testhost.exe"
20 +mt.exe -manifest "WixToolsetTest.BurnE2E\testhost.longpathaware.manifest" -updateresource:"%_B%\netcoreapp3.1\testhost.exe"
21 +
22 @if not "%RuntimeTestsEnabled%"=="true" goto :LExit
23
24 dotnet test -c %_C% --no-build WixToolsetTest.BurnE2E -l "trx;LogFileName=%_L%\TestResults\WixToolsetTest.BurnE2E.trx" || exit /b
src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+4
@@ -231,6 +231,10 @@ namespace WixToolset.Core.Burn.Bundles
231 writer.WriteEndElement();
232 }
233
234 + writer.WriteStartElement("longPathAware", ws2016Namespace);
235 + writer.WriteString("true");
236 + writer.WriteEndElement();
237 +
238 writer.WriteEndElement(); // </windowSettings>
239 writer.WriteEndElement(); // </application>
240 }
src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+8 -2
@@ -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>" +
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>" +
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>" +
176 "</assembly>", actualManifestData);
177 }
178 }
@@ -215,7 +218,10 @@ namespace WixToolsetTest.CoreIntegration
218 "<dependency><dependentAssembly><assemblyIdentity name=\"Microsoft.Windows.Common-Controls\" version=\"6.0.0.0\" processorArchitecture=\"amd64\" publicKeyToken=\"6595b64144ccf1df\" language=\"*\" type=\"win32\" /></dependentAssembly></dependency>" +
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>" +
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>" +
225 "</assembly>", actualManifestData);
226
227 var extractResult = BundleExtractor.ExtractAllContainers(null, exePath, baFolderPath, attachedFolderPath, extractFolderPath);