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);