@joebigelow / wix-1 / commits / ea3d1859

Improved cabinet handling

Rob Mensching committed Nov 29, 2017 at 14:08 UTC ea3d18595a610ee07b03f07af4f03cf75b5ab420
25 files changed +1276 -8
WixToolset.Core.Native.sln
+37 -1
@@ -1,7 +1,7 @@
1 
2 Microsoft Visual Studio Solution File, Format Version 12.00
3 # Visual Studio 15
4 -VisualStudioVersion = 15.0.26730.12
4 +VisualStudioVersion = 15.0.27004.2009
5 MinimumVisualStudioVersion = 15.0.26124.0
6 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winterop", "src\winterop\winterop.vcxproj", "{26D45E58-E703-431D-B67E-493C72C9DA0B}"
7 EndProject
@@ -10,28 +10,64 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolset.Core.Native", "s
10 {26D45E58-E703-431D-B67E-493C72C9DA0B} = {26D45E58-E703-431D-B67E-493C72C9DA0B}
11 EndProjectSection
12 EndProject
13 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wixnative", "src\wixnative\wixnative.vcxproj", "{8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}"
14 +EndProject
15 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WixToolsetTest.Core.Native", "src\test\WixToolsetTest.Core.Native\WixToolsetTest.Core.Native.csproj", "{54F5329A-C113-471A-8EE1-83021E8A4853}"
16 +EndProject
17 Global
18 GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 Debug|Any CPU = Debug|Any CPU
20 + Debug|x64 = Debug|x64
21 Debug|x86 = Debug|x86
22 Release|Any CPU = Release|Any CPU
23 + Release|x64 = Release|x64
24 Release|x86 = Release|x86
25 EndGlobalSection
26 GlobalSection(ProjectConfigurationPlatforms) = postSolution
27 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Debug|Any CPU.ActiveCfg = Debug|Win32
28 + {26D45E58-E703-431D-B67E-493C72C9DA0B}.Debug|x64.ActiveCfg = Debug|x64
29 + {26D45E58-E703-431D-B67E-493C72C9DA0B}.Debug|x64.Build.0 = Debug|x64
30 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Debug|x86.ActiveCfg = Debug|Win32
31 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Debug|x86.Build.0 = Debug|Win32
32 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Release|Any CPU.ActiveCfg = Release|Win32
33 + {26D45E58-E703-431D-B67E-493C72C9DA0B}.Release|x64.ActiveCfg = Release|x64
34 + {26D45E58-E703-431D-B67E-493C72C9DA0B}.Release|x64.Build.0 = Release|x64
35 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Release|x86.ActiveCfg = Release|Win32
36 {26D45E58-E703-431D-B67E-493C72C9DA0B}.Release|x86.Build.0 = Release|Win32
37 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 + {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|x64.ActiveCfg = Debug|Any CPU
40 + {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|x64.Build.0 = Debug|Any CPU
41 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|x86.ActiveCfg = Debug|Any CPU
42 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Debug|x86.Build.0 = Debug|Any CPU
43 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|Any CPU.ActiveCfg = Release|Any CPU
44 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|Any CPU.Build.0 = Release|Any CPU
45 + {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|x64.ActiveCfg = Release|Any CPU
46 + {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|x64.Build.0 = Release|Any CPU
47 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|x86.ActiveCfg = Release|Any CPU
48 {C1F36B7C-6A5B-44CB-BD05-3C9CDEC2DD63}.Release|x86.Build.0 = Release|Any CPU
49 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Debug|Any CPU.ActiveCfg = Debug|Win32
50 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Debug|x64.ActiveCfg = Debug|x64
51 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Debug|x64.Build.0 = Debug|x64
52 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Debug|x86.ActiveCfg = Debug|Win32
53 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Debug|x86.Build.0 = Debug|Win32
54 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Release|Any CPU.ActiveCfg = Release|Win32
55 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Release|x64.ActiveCfg = Release|x64
56 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Release|x64.Build.0 = Release|x64
57 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Release|x86.ActiveCfg = Release|Win32
58 + {8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}.Release|x86.Build.0 = Release|Win32
59 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|Any CPU.Build.0 = Debug|Any CPU
61 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|x64.ActiveCfg = Debug|Any CPU
62 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|x64.Build.0 = Debug|Any CPU
63 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|x86.ActiveCfg = Debug|Any CPU
64 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Debug|x86.Build.0 = Debug|Any CPU
65 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|Any CPU.ActiveCfg = Release|Any CPU
66 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|Any CPU.Build.0 = Release|Any CPU
67 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|x64.ActiveCfg = Release|Any CPU
68 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|x64.Build.0 = Release|Any CPU
69 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|x86.ActiveCfg = Release|Any CPU
70 + {54F5329A-C113-471A-8EE1-83021E8A4853}.Release|x86.Build.0 = Release|Any CPU
71 EndGlobalSection
72 GlobalSection(SolutionProperties) = preSolution
73 HideSolutionNode = FALSE
appveyor.cmd
+2 -3
@@ -3,10 +3,9 @@
3
4 nuget restore
5
6 -msbuild -p:Configuration=Release;Platform=Win32 -t:PackNativeNuget src\winterop\winterop.vcxproj
7 -msbuild -p:Configuration=Release;Platform=x64 -t:PackNativeNuget src\winterop\winterop.vcxproj
6 +msbuild -p:Configuration=Release .\src\test\WixToolsetTest.Core.Native\WixToolsetTest.Core.Native.csproj
7
9 -dotnet pack -c Release .\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj
8 +msbuild -t:Pack -p:Configuration=Release .\src\WixToolset.Core.Native\WixToolset.Core.Native.csproj
9
10 @popd
11 @endlocal
\ No newline at end of file
src/WixToolset.Core.Native/Cabinet.cs new
+199
@@ -0,0 +1,199 @@
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 WixToolset.Core.Native
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Linq;
8 +
9 + /// <summary>
10 + /// Wrapper class around interop with wixcab.dll to compress files into a cabinet.
11 + /// </summary>
12 + public sealed class Cabinet
13 + {
14 + private const string CompressionLevelVariable = "WIX_COMPRESSION_LEVEL";
15 + private static readonly char[] TextLineSplitter = new[] { '\t' };
16 +
17 + public Cabinet(string path)
18 + {
19 + this.Path = path;
20 + }
21 +
22 + public string Path { get; }
23 +
24 + /// <summary>
25 + /// Creates a cabinet.
26 + /// </summary>
27 + /// <param name="cabPath">Path of cabinet to create.</param>
28 + /// <param name="compressionLevel">Level of compression to apply.</param>
29 + /// <param name="maxFiles">Maximum number of files that will be added to cabinet.</param>
30 + /// <param name="maxSize">Maximum size of cabinet.</param>
31 + /// <param name="maxThresh">Maximum threshold for each cabinet.</param>
32 + public void Compress(IEnumerable<CabinetCompressFile> files, CabinetCompressionLevel compressionLevel, int maxSize = 0, int maxThresh = 0)
33 + {
34 + var compressionLevelVariable = Environment.GetEnvironmentVariable(CompressionLevelVariable);
35 +
36 + // Override authored compression level if environment variable is present.
37 + if (!String.IsNullOrEmpty(compressionLevelVariable))
38 + {
39 + if (!Enum.TryParse(compressionLevelVariable, true, out compressionLevel))
40 + {
41 + //throw new WixException(WixErrors.IllegalEnvironmentVariable(CompressionLevelVariable, compressionLevelVariable));
42 + throw new ArgumentException();
43 + }
44 + }
45 +
46 + var wixnative = new WixNativeExe("smartcab", this.Path, Convert.ToInt32(compressionLevel), files.Count(), maxSize, maxThresh);
47 +
48 + foreach (var file in files)
49 + {
50 + wixnative.AddStdinLine(file.ToWixNativeStdinLine());
51 + }
52 +
53 + wixnative.Run();
54 +
55 +#if TOOD_ERROR_HANDLING
56 + catch (COMException ce)
57 + {
58 + // If we get a "the file exists" error, we must have a full temp directory - so report the issue
59 + if (0x80070050 == unchecked((uint)ce.ErrorCode))
60 + {
61 + throw new WixException(WixErrors.FullTempDirectory("WSC", Path.GetTempPath()));
62 + }
63 +
64 + throw;
65 + }
66 +#endif
67 + }
68 +
69 + /// <summary>
70 + /// Enumerates all files in a cabinet.
71 + /// </summary>
72 + /// <returns>>List of CabinetFileInfo</returns>
73 + public List<CabinetFileInfo> Enumerate()
74 + {
75 + var wixnative = new WixNativeExe("enumcab", this.Path);
76 + var lines = wixnative.Run();
77 +
78 + var fileInfoList = new List<CabinetFileInfo>();
79 +
80 + foreach (var line in lines)
81 + {
82 + if (String.IsNullOrEmpty(line))
83 + {
84 + continue;
85 + }
86 +
87 + var data = line.Split(TextLineSplitter, StringSplitOptions.None);
88 +
89 + var size = Convert.ToInt32(data[1]);
90 + var date = Convert.ToInt32(data[2]);
91 + var time = Convert.ToInt32(data[3]);
92 +
93 + fileInfoList.Add(new CabinetFileInfo(data[0], size, date, time));
94 + }
95 +
96 + return fileInfoList;
97 + }
98 +
99 + /// <summary>
100 + /// Extracts all the files from a cabinet to a directory.
101 + /// </summary>
102 + /// <param name="outputFolder">Directory to extract files to.</param>
103 + public void Extract(string outputFolder)
104 + {
105 + if (!outputFolder.EndsWith("\\", StringComparison.Ordinal))
106 + {
107 + outputFolder += "\\";
108 + }
109 +
110 + var wixnative = new WixNativeExe("extractcab", this.Path, outputFolder);
111 + wixnative.Run();
112 + }
113 +
114 +#if TOOD_ERROR_HANDLING
115 + /// <summary>
116 + /// Adds a file to the cabinet with an optional MSI file hash.
117 + /// </summary>
118 + /// <param name="file">The file to add.</param>
119 + /// <param name="token">The token for the file.</param>
120 + /// <param name="fileHash">The MSI file hash of the file.</param>
121 + //private void AddFile(string file, string token, MsiInterop.MSIFILEHASHINFO fileHash)
122 + //{
123 + // try
124 + // {
125 + // NativeMethods.CreateCabAddFile(file, token, fileHash, this.handle);
126 + // }
127 + // catch (COMException ce)
128 + // {
129 + // if (0x80004005 == unchecked((uint)ce.ErrorCode)) // E_FAIL
130 + // {
131 + // throw new WixException(WixErrors.CreateCabAddFileFailed());
132 + // }
133 + // else if (0x80070070 == unchecked((uint)ce.ErrorCode)) // ERROR_DISK_FULL
134 + // {
135 + // throw new WixException(WixErrors.CreateCabInsufficientDiskSpace());
136 + // }
137 + // else
138 + // {
139 + // throw;
140 + // }
141 + // }
142 + // catch (DirectoryNotFoundException)
143 + // {
144 + // throw new WixFileNotFoundException(file);
145 + // }
146 + // catch (FileNotFoundException)
147 + // {
148 + // throw new WixFileNotFoundException(file);
149 + // }
150 + //}
151 +
152 + /// <summary>
153 + /// Complete/commit the cabinet - this must be called before Dispose so that errors will be
154 + /// reported on the same thread.
155 + /// </summary>
156 + /// <param name="newCabNamesCallBackAddress">Address of Binder's callback function for Cabinet Splitting</param>
157 + public void Complete(IntPtr newCabNamesCallBackAddress)
158 + {
159 + if (IntPtr.Zero != this.handle)
160 + {
161 + try
162 + {
163 + if (newCabNamesCallBackAddress != IntPtr.Zero && this.maxSize != 0)
164 + {
165 + NativeMethods.CreateCabFinish(this.handle, newCabNamesCallBackAddress);
166 + }
167 + else
168 + {
169 + NativeMethods.CreateCabFinish(this.handle, IntPtr.Zero);
170 + }
171 +
172 + GC.SuppressFinalize(this);
173 + this.disposed = true;
174 + }
175 + catch (COMException ce)
176 + {
177 + //if (0x80004005 == unchecked((uint)ce.ErrorCode)) // E_FAIL
178 + //{
179 + // // This error seems to happen, among other situations, when cabbing more than 0xFFFF files
180 + // throw new WixException(WixErrors.FinishCabFailed());
181 + //}
182 + //else if (0x80070070 == unchecked((uint)ce.ErrorCode)) // ERROR_DISK_FULL
183 + //{
184 + // throw new WixException(WixErrors.CreateCabInsufficientDiskSpace());
185 + //}
186 + //else
187 + //{
188 + // throw;
189 + //}
190 + }
191 + finally
192 + {
193 + this.handle = IntPtr.Zero;
194 + }
195 + }
196 + }
197 +#endif
198 + }
199 +}
src/WixToolset.Core.Native/CabinetCompressFile.cs new
+65
@@ -0,0 +1,65 @@
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 WixToolset.Core.Native
4 +{
5 + /// <summary>
6 + /// Information to compress file into a cabinet.
7 + /// </summary>
8 + public sealed class CabinetCompressFile
9 + {
10 + /// <summary>
11 + /// Cabinet compress file.
12 + /// </summary>
13 + /// <param name="path">Path to file to add.</param>
14 + /// <param name="token">The token for the file.</param>
15 + public CabinetCompressFile(string path, string token)
16 + {
17 + this.Path = path;
18 + this.Token = token;
19 + this.Hash = null;
20 + }
21 +
22 + /// <summary>
23 + /// Cabinet compress file.
24 + /// </summary>
25 + /// <param name="path">Path to file to add.</param>
26 + /// <param name="token">The token for the file.</param>
27 + /// <param name="hash1">Hash 1</param>
28 + /// <param name="hash2">Hash 2</param>
29 + /// <param name="hash3">Hash 3</param>
30 + /// <param name="hash4">Hash 4</param>
31 + public CabinetCompressFile(string path, string token, int hash1, int hash2, int hash3, int hash4)
32 + {
33 + this.Path = path;
34 + this.Token = token;
35 + this.Hash = new[] { hash1, hash2, hash3, hash4 };
36 + }
37 +
38 + /// <summary>
39 + /// Gets the path to the file to compress.
40 + /// </summary>
41 + public string Path { get; }
42 +
43 + /// <summary>
44 + /// Gets the token for the file to compress.
45 + /// </summary>
46 + public string Token { get; }
47 +
48 + /// <summary>
49 + /// Gets the hash of the file to compress.
50 + /// </summary>
51 + public int[] Hash { get; }
52 +
53 + internal string ToWixNativeStdinLine()
54 + {
55 + if (this.Hash == null)
56 + {
57 + return $"{this.Path}\t{this.Token}";
58 + }
59 + else
60 + {
61 + return $"{this.Path}\t{this.Token}\t{this.Hash[0]}\t{this.Hash[1]}\t{this.Hash[2]}\t{this.Hash[3]}";
62 + }
63 + }
64 + }
65 +}
src/WixToolset.Core.Native/CabinetCompressionLevel.cs new
+25
@@ -0,0 +1,25 @@
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 WixToolset.Core.Native
4 +{
5 + /// <summary>
6 + /// Compression level to use when creating cabinet.
7 + /// </summary>
8 + public enum CabinetCompressionLevel
9 + {
10 + /// <summary>Use no compression.</summary>
11 + None,
12 +
13 + /// <summary>Use low compression.</summary>
14 + Low,
15 +
16 + /// <summary>Use medium compression.</summary>
17 + Medium,
18 +
19 + /// <summary>Use high compression.</summary>
20 + High,
21 +
22 + /// <summary>Use ms-zip compression.</summary>
23 + Mszip
24 + }
25 +}
\ No newline at end of file
src/WixToolset.Core.Native/CabinetFileInfo.cs new
+67
@@ -0,0 +1,67 @@
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 WixToolset.Core.Native
4 +{
5 + using System;
6 +
7 + /// <summary>
8 + /// Properties of a file in a cabinet.
9 + /// </summary>
10 + public sealed class CabinetFileInfo
11 + {
12 + /// <summary>
13 + /// Constructs CabinetFileInfo
14 + /// </summary>
15 + /// <param name="fileId">File Id</param>
16 + /// <param name="size">Size of file</param>
17 + /// <param name="date">Last modified date</param>
18 + /// <param name="time">Last modified time</param>
19 + public CabinetFileInfo(string fileId, int size, int date, int time)
20 + {
21 + this.FileId = fileId;
22 + this.Size = size;
23 + this.Date = date;
24 + this.Time = time;
25 + }
26 +
27 + /// <summary>
28 + /// Gets the file Id of the file.
29 + /// </summary>
30 + /// <value>file Id</value>
31 + public string FileId { get; }
32 +
33 + /// <summary>
34 + /// Gets modified date (DOS format).
35 + /// </summary>
36 + public int Date { get; }
37 +
38 + /// <summary>
39 + /// Gets modified time (DOS format).
40 + /// </summary>
41 + public int Time { get; }
42 +
43 + /// <summary>
44 + /// Gets the size of the file in bytes.
45 + /// </summary>
46 + public int Size { get; }
47 +
48 + /// <summary>
49 + /// Compares this file info's date and time with another datetime.
50 + /// </summary>
51 + /// <param name="dateTime">Date and time to compare with/</param>
52 + /// <returns>
53 + /// For some reason DateTime.ToLocalTime() does not match kernel32.dll FileTimeToLocalFileTime().
54 + /// Since cabinets store date and time with the kernel32.dll functions, we need to convert DateTime
55 + /// to local file time using the kernel32.dll functions.
56 + /// </returns>
57 + public bool SameAsDateTime(DateTime dateTime)
58 + {
59 + long filetime = dateTime.ToFileTime();
60 + long localTime = 0;
61 + NativeMethods.FileTimeToLocalFileTime(ref filetime, ref localTime);
62 + NativeMethods.FileTimeToDosDateTime(ref localTime, out var cabDate, out var cabTime);
63 +
64 + return this.Date == cabDate && this.Time == cabTime;
65 + }
66 + }
67 +}
src/WixToolset.Core.Native/WixNativeExe.cs new
+115
@@ -0,0 +1,115 @@
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 WixToolset.Core.Native
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.ComponentModel;
8 + using System.Diagnostics;
9 + using System.IO;
10 + using System.Reflection;
11 +
12 + internal class WixNativeExe
13 + {
14 + private const int FiveMinutesInMilliseconds = 300000;
15 + private static readonly string PathToWixNativeExe;
16 +
17 + private readonly string commandLine;
18 + private readonly List<string> stdinLines = new List<string>();
19 +
20 + static WixNativeExe()
21 + {
22 + PathToWixNativeExe = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), "x86\\wixnative.exe");
23 + }
24 +
25 + public WixNativeExe(params object[] args)
26 + {
27 + this.commandLine = String.Join(" ", QuoteArgumentsAsNecesary(args));
28 + }
29 +
30 + public void AddStdinLine(string line)
31 + {
32 + this.stdinLines.Add(line);
33 + }
34 +
35 + public void AddStdinLines(IEnumerable<string> lines)
36 + {
37 + this.stdinLines.AddRange(lines);
38 + }
39 +
40 + public IEnumerable<string> Run()
41 + {
42 + var wixNativeInfo = new ProcessStartInfo(PathToWixNativeExe, this.commandLine)
43 + {
44 + RedirectStandardInput = true,
45 + RedirectStandardOutput = true,
46 + CreateNoWindow = true,
47 + ErrorDialog = false,
48 + UseShellExecute = false
49 + };
50 +
51 + var stdoutLines = new List<string>();
52 +
53 + using (var process = Process.Start(wixNativeInfo))
54 + {
55 + process.OutputDataReceived += (s, a) => stdoutLines.Add(a.Data);
56 + process.BeginOutputReadLine();
57 +
58 + if (this.stdinLines.Count > 0)
59 + {
60 + foreach (var line in this.stdinLines)
61 + {
62 + process.StandardInput.WriteLine(line);
63 + }
64 +
65 + // Trailing blank line indicates stdin complete.
66 + process.StandardInput.WriteLine();
67 + }
68 +
69 + if (process.WaitForExit(FiveMinutesInMilliseconds))
70 + {
71 + // If the process successfully exits documentation says we need to wait again
72 + // without a timeout to ensure that all of the redirected output is captured.
73 + //
74 + process.WaitForExit();
75 + }
76 +
77 + if (process.ExitCode != 0)
78 + {
79 + throw new Win32Exception(process.ExitCode);
80 + }
81 + }
82 +
83 + return stdoutLines;
84 + }
85 +
86 + private static IEnumerable<string> QuoteArgumentsAsNecesary(object[] args)
87 + {
88 + foreach (var arg in args)
89 + {
90 + if (arg is string str)
91 + {
92 + if (String.IsNullOrEmpty(str))
93 + {
94 + }
95 + else if (str.Contains(" ") && !str.StartsWith("\""))
96 + {
97 + yield return $"\"{str}\"";
98 + }
99 + else
100 + {
101 + yield return str;
102 + }
103 + }
104 + else if (arg is int i)
105 + {
106 + yield return i.ToString();
107 + }
108 + else
109 + {
110 + throw new ArgumentException(nameof(args));
111 + }
112 + }
113 + }
114 + }
115 +}
src/WixToolset.Core.Native/WixToolset.Core.Native.csproj
+21 -3
@@ -3,6 +3,7 @@
3 <PropertyGroup>
4 <TargetFramework>netstandard2.0</TargetFramework>
5 <NuspecFile>$(MSBuildThisFileName).nuspec</NuspecFile>
6 + <Description>Core Native</Description>
7 <!-- <BeforePack>SetNuspecProperties</BeforePack> -->
8 </PropertyGroup>
9
@@ -10,9 +11,26 @@
11 <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" />
12 </ItemGroup>
13
13 - <!-- <ItemGroup>
14 - <ProjectReference Include="..\winterop\winterop.vcxproj" ExcludeAssets="All" />
15 - </ItemGroup> -->
14 + <Target Name="BuildWixNative"
15 + BeforeTargets="GetCopyToOutputDirectoryItems">
16 + <MSBuild Projects="..\wixnative\wixnative.vcxproj" Properties="Platform=Win32" />
17 + <MSBuild Projects="..\wixnative\wixnative.vcxproj" Properties="Platform=x64" />
18 +
19 + <ItemGroup>
20 + <NativeProjectOutput Include="$(OutputPath)..\Win32\*.exe;$(OutputPath)..\Win32\*.pdb">
21 + <TargetRelativeFolder>x86\</TargetRelativeFolder>
22 + </NativeProjectOutput>
23 + <NativeProjectOutput Include="$(OutputPath)..\x64\*.exe;$(OutputPath)..\x64\*.pdb">
24 + <TargetRelativeFolder>x64\</TargetRelativeFolder>
25 + </NativeProjectOutput>
26 +
27 + <AllItemsFullPathWithTargetPath Include="@(NativeProjectOutput->'%(FullPath)')">
28 + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
29 + <TargetPath>%(TargetRelativeFolder)%(Filename)%(Extension)</TargetPath>
30 + </AllItemsFullPathWithTargetPath>
31 + </ItemGroup>
32 + </Target>
33 +
34 <Target Name="SetNuspecProperties"
35 AfterTargets="Build">
36 <PropertyGroup>
src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec
+6 -1
@@ -10,15 +10,20 @@
10 <requireLicenseAcceptance>false</requireLicenseAcceptance>
11 <description>$description$</description>
12 <copyright>$copyright$</copyright>
13 -
13 +<!--
14 <dependencies>
15 <dependency id="runtime.win-x86.WixToolset.Core.Native" version="$version$" />
16 <dependency id="runtime.win-x64.WixToolset.Core.Native" version="$version$" />
17 </dependencies>
18 + -->
19 </metadata>
20
21 <files>
22 <file src="$id$.dll" target="lib\netstandard2.0" />
23 <file src="$id$.pdb" target="lib\netstandard2.0" />
24 + <file src="..\Win32\wixnative.exe" target="lib\netstandard2.0\x86" />
25 + <file src="..\Win32\wixnative.pdb" target="lib\netstandard2.0\x86" />
26 + <file src="..\x64\wixnative.exe" target="lib\netstandard2.0\x64" />
27 + <file src="..\x64\wixnative.pdb" target="lib\netstandard2.0\x64" />
28 </files>
29 </package>
src/test/WixToolsetTest.Core.Native/CabinetFixture.cs new
+115
@@ -0,0 +1,115 @@
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.CoreNative
4 +{
5 + using System;
6 + using System.IO;
7 + using System.Linq;
8 + using WixToolset.Core.Native;
9 + using WixToolsetTest.CoreNative.Utility;
10 + using Xunit;
11 +
12 + public class CabinetFixture
13 + {
14 + [Fact]
15 + public void CanCreateSingleFileCabinet()
16 + {
17 + using (var fs = new DisposableFileSystem())
18 + {
19 + var intermediateFolder = fs.GetFolder(true);
20 + var cabPath = Path.Combine(intermediateFolder, "testout.cab");
21 +
22 + var files = new[] { new CabinetCompressFile(TestData.Get(@"TestData\test.txt"), "test.txt") };
23 +
24 + var cabinet = new Cabinet(cabPath);
25 + cabinet.Compress(files, CabinetCompressionLevel.Low);
26 +
27 + Assert.True(File.Exists(cabPath));
28 + }
29 + }
30 +
31 + [Fact]
32 + public void CanEnumerateSingleFileCabinet()
33 + {
34 + var cabinetPath = TestData.Get(@"TestData\test.cab");
35 +
36 + var cabinet = new Cabinet(cabinetPath);
37 + var files = cabinet.Enumerate();
38 +
39 + var file = files.Single();
40 + Assert.Equal("test.txt", file.FileId);
41 + Assert.Equal(17, file.Size);
42 +
43 + Assert.Equal(19259, file.Date);
44 + Assert.Equal(47731, file.Time);
45 + Assert.True(file.SameAsDateTime(new DateTime(2017, 9, 28, 0, 19, 38)));
46 + }
47 +
48 + [Fact]
49 + public void CanExtractSingleFileCabinet()
50 + {
51 + var cabinetPath = TestData.Get(@"TestData\test.cab");
52 +
53 + using (var fs = new DisposableFileSystem())
54 + {
55 + var extractFolder = fs.GetFolder(true);
56 +
57 + var cabinet = new Cabinet(cabinetPath);
58 + cabinet.Extract(extractFolder);
59 +
60 + var files = Directory.EnumerateFiles(extractFolder);
61 +
62 + var file = new FileInfo(files.Single());
63 + CabInterop.DateTimeToCabDateAndTime(file.CreationTime, out var date, out var time);
64 +
65 + Assert.Equal("test.txt", file.Name);
66 + Assert.Equal(17, file.Length);
67 + Assert.Equal(19259, date);
68 + Assert.Equal(47731, time);
69 + }
70 + }
71 +
72 + [Fact]
73 + public void IntegrationTest()
74 + {
75 + using (var fs = new DisposableFileSystem())
76 + {
77 + var intermediateFolder = fs.GetFolder(true);
78 + var cabinetPath = Path.Combine(intermediateFolder, "testout.cab");
79 + var extractFolder = fs.GetFolder(true);
80 +
81 + // Compress.
82 + {
83 + var files = new[] { new CabinetCompressFile(TestData.Get(@"TestData\test.txt"), "test.txt") };
84 +
85 + var cabinet = new Cabinet(cabinetPath);
86 + cabinet.Compress(files, CabinetCompressionLevel.Low);
87 + }
88 +
89 + // Extract.
90 + {
91 + var cabinet = new Cabinet(cabinetPath);
92 + cabinet.Extract(extractFolder);
93 + }
94 +
95 + // Enumerate to compare cabinet to extracted files.
96 + {
97 + var cabinet = new Cabinet(cabinetPath);
98 + var enumerated = cabinet.Enumerate().OrderBy(f => f.FileId).ToArray();
99 +
100 + var files = Directory.EnumerateFiles(extractFolder).OrderBy(f => f).ToArray();
101 +
102 + for (var i =0; i < enumerated.Length; ++i)
103 + {
104 + var cabFileInfo = enumerated[i];
105 + var fileInfo = new FileInfo(files[i]);
106 +
107 + Assert.Equal(cabFileInfo.FileId, fileInfo.Name);
108 + Assert.Equal(cabFileInfo.Size, fileInfo.Length);
109 + Assert.True(cabFileInfo.SameAsDateTime(fileInfo.CreationTime));
110 + }
111 + }
112 + }
113 + }
114 + }
115 +}
src/test/WixToolsetTest.Core.Native/TestData/test.cab
Binary files /dev/null and b/src/test/WixToolsetTest.Core.Native/TestData/test.cab differ
src/test/WixToolsetTest.Core.Native/TestData/test.txt new
+1
@@ -0,0 +1 @@
1 +This is test.txt.
\ No newline at end of file
src/test/WixToolsetTest.Core.Native/Utility/DisposableFileSystem.cs new
+86
@@ -0,0 +1,86 @@
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.CoreNative.Utility
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.IO;
8 +
9 + public class DisposableFileSystem : IDisposable
10 + {
11 + protected bool Disposed { get; private set; }
12 +
13 + private List<string> CleanupPaths { get; } = new List<string>();
14 +
15 + public string GetFile(bool create = false)
16 + {
17 + var path = Path.GetTempFileName();
18 +
19 + if (!create)
20 + {
21 + File.Delete(path);
22 + }
23 +
24 + this.CleanupPaths.Add(path);
25 +
26 + return path;
27 + }
28 +
29 + public string GetFolder(bool create = false)
30 + {
31 + var path = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
32 +
33 + if (create)
34 + {
35 + Directory.CreateDirectory(path);
36 + }
37 +
38 + this.CleanupPaths.Add(path);
39 +
40 + return path;
41 + }
42 +
43 +
44 + #region // IDisposable
45 +
46 + public void Dispose()
47 + {
48 + this.Dispose(true);
49 + GC.SuppressFinalize(this);
50 + }
51 +
52 + protected virtual void Dispose(bool disposing)
53 + {
54 + if (this.Disposed)
55 + {
56 + return;
57 + }
58 +
59 + if (disposing)
60 + {
61 + foreach (var path in this.CleanupPaths)
62 + {
63 + try
64 + {
65 + if (File.Exists(path))
66 + {
67 + File.Delete(path);
68 + }
69 + else if (Directory.Exists(path))
70 + {
71 + Directory.Delete(path, true);
72 + }
73 + }
74 + catch
75 + {
76 + // Best effort delete, so ignore any failures.
77 + }
78 + }
79 + }
80 +
81 + this.Disposed = true;
82 + }
83 +
84 + #endregion
85 + }
86 +}
src/test/WixToolsetTest.Core.Native/Utility/Pushd.cs new
+46
@@ -0,0 +1,46 @@
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.CoreNative.Utility
4 +{
5 + using System;
6 + using System.IO;
7 +
8 + public class Pushd : IDisposable
9 + {
10 + protected bool Disposed { get; private set; }
11 +
12 + public Pushd(string path)
13 + {
14 + this.PreviousDirectory = Directory.GetCurrentDirectory();
15 +
16 + Directory.SetCurrentDirectory(path);
17 + }
18 +
19 + public string PreviousDirectory { get; }
20 +
21 + #region // IDisposable
22 +
23 + public void Dispose()
24 + {
25 + this.Dispose(true);
26 + GC.SuppressFinalize(this);
27 + }
28 +
29 + protected virtual void Dispose(bool disposing)
30 + {
31 + if (this.Disposed)
32 + {
33 + return;
34 + }
35 +
36 + if (disposing)
37 + {
38 + Directory.SetCurrentDirectory(this.PreviousDirectory);
39 + }
40 +
41 + this.Disposed = true;
42 + }
43 +
44 + #endregion
45 + }
46 +}
src/test/WixToolsetTest.Core.Native/Utility/TestData.cs new
+17
@@ -0,0 +1,17 @@
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.CoreNative.Utility
4 +{
5 + using System;
6 + using System.IO;
7 +
8 + public class TestData
9 + {
10 + public static string LocalPath => Path.GetDirectoryName(new Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);
11 +
12 + public static string Get(params string[] paths)
13 + {
14 + return Path.Combine(LocalPath, Path.Combine(paths));
15 + }
16 + }
17 +}
src/test/WixToolsetTest.Core.Native/WixToolsetTest.Core.Native.csproj new
+24
@@ -0,0 +1,24 @@
1 +<?xml version="1.0" encoding="utf-8"?>
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 +<Project Sdk="Microsoft.NET.Sdk">
5 + <PropertyGroup>
6 + <TargetFramework>netcoreapp2.0</TargetFramework>
7 + <IsPackable>false</IsPackable>
8 + </PropertyGroup>
9 +
10 + <ItemGroup>
11 + <Content Include="TestData\test.cab" CopyToOutputDirectory="PreserveNewest" />
12 + <Content Include="TestData\test.txt" CopyToOutputDirectory="PreserveNewest" />
13 + </ItemGroup>
14 +
15 + <ItemGroup>
16 + <ProjectReference Include="..\..\WixToolset.Core.Native\WixToolset.Core.Native.csproj" />
17 + </ItemGroup>
18 +
19 + <ItemGroup>
20 + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
21 + <PackageReference Include="xunit" Version="2.2.0" />
22 + <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
23 + </ItemGroup>
24 +</Project>
src/wixnative/enumcab.cpp new
+47
@@ -0,0 +1,47 @@
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 +#include "precomp.h"
4 +
5 +static INT_PTR __stdcall EnumCallback(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin);
6 +
7 +
8 +HRESULT EnumCabCommand(
9 + __in int argc,
10 + __in LPWSTR argv[]
11 +)
12 +{
13 + HRESULT hr = E_INVALIDARG;
14 + LPCWSTR wzCabPath = NULL;
15 +
16 + if (argc < 1)
17 + {
18 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: cabPath outputFolder");
19 + }
20 +
21 + wzCabPath = argv[0];
22 +
23 + hr = CabInitialize(FALSE);
24 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath);
25 +
26 + hr = CabEnumerate(wzCabPath, L"*", EnumCallback, 0);
27 + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath);
28 +
29 +LExit:
30 + CabUninitialize();
31 +
32 + return hr;
33 +}
34 +
35 +
36 +static INT_PTR __stdcall EnumCallback(
37 + __in FDINOTIFICATIONTYPE fdint,
38 + __in PFDINOTIFICATION pfdin
39 +)
40 +{
41 + if (fdint == fdintCOPY_FILE)
42 + {
43 + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%s\t%d\t%u\t%u", pfdin->psz1, pfdin->cb, pfdin->date, pfdin->time);
44 + }
45 +
46 + return 0;
47 +}
src/wixnative/extractcab.cpp new
+50
@@ -0,0 +1,50 @@
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 +#include "precomp.h"
4 +
5 +static HRESULT ProgressCallback(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
6 +
7 +
8 +HRESULT ExtractCabCommand(
9 + __in int argc,
10 + __in LPWSTR argv[]
11 +)
12 +{
13 + HRESULT hr = E_INVALIDARG;
14 + LPCWSTR wzCabPath = NULL;
15 + LPCWSTR wzOutputFolder = NULL;
16 +
17 + if (argc < 2)
18 + {
19 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: cabPath outputFolder");
20 + }
21 +
22 + wzCabPath = argv[0];
23 + wzOutputFolder = argv[1];
24 +
25 + hr = CabInitialize(FALSE);
26 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath);
27 +
28 + hr = CabExtract(wzCabPath, L"*", wzOutputFolder, ProgressCallback, NULL, 0);
29 + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath);
30 +
31 +LExit:
32 + CabUninitialize();
33 +
34 + return hr;
35 +}
36 +
37 +
38 +static HRESULT ProgressCallback(
39 + __in BOOL fBeginFile,
40 + __in LPCWSTR wzFileId,
41 + __in LPVOID /*pvContext*/
42 +)
43 +{
44 + if (fBeginFile)
45 + {
46 + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls", wzFileId);
47 + }
48 +
49 + return S_OK;
50 +}
src/wixnative/packages.config new
+5
@@ -0,0 +1,5 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<packages>
3 + <package id="Nerdbank.GitVersioning" version="2.0.41" targetFramework="native" developmentDependency="true" />
4 + <package id="WixToolset.DUtil" version="4.0.3" targetFramework="native" />
5 +</packages>
\ No newline at end of file
src/wixnative/precomp.cpp new
+3
@@ -0,0 +1,3 @@
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 +#include "precomp.h"
src/wixnative/precomp.h new
+19
@@ -0,0 +1,19 @@
1 +#pragma once
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 +#include <windows.h>
5 +#include <aclapi.h>
6 +#include <mergemod.h>
7 +
8 +#include "dutil.h"
9 +#include "conutil.h"
10 +#include "memutil.h"
11 +#include "pathutil.h"
12 +#include "strutil.h"
13 +#include "cabcutil.h"
14 +#include "cabutil.h"
15 +
16 +HRESULT SmartCabCommand(int argc, LPWSTR argv[]);
17 +HRESULT ResetAclsCommand(int argc, LPWSTR argv[]);
18 +HRESULT EnumCabCommand(int argc, LPWSTR argv[]);
19 +HRESULT ExtractCabCommand(int argc, LPWSTR argv[]);
src/wixnative/resetacls.cpp new
+51
@@ -0,0 +1,51 @@
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 +#include "precomp.h"
4 +
5 +HRESULT ResetAclsCommand(int argc, LPWSTR argv[])
6 +{
7 + Unused(argc);
8 + Unused(argv);
9 +
10 + HRESULT hr = S_OK;
11 + ACL* pacl = NULL;
12 + DWORD cbAcl = sizeof(ACL);
13 + LPWSTR sczFilePath = NULL;
14 +
15 + // create an empty (not NULL!) ACL to use on all the files
16 + pacl = static_cast<ACL*>(MemAlloc(cbAcl, FALSE));
17 + ConsoleExitOnNull(pacl, hr, E_OUTOFMEMORY, CONSOLE_COLOR_RED, "failed to allocate ACL");
18 +
19 +#pragma prefast(push)
20 +#pragma prefast(disable:25029)
21 + if (!::InitializeAcl(pacl, cbAcl, ACL_REVISION))
22 +#pragma prefast(op)
23 + {
24 + ConsoleExitOnLastError(hr, CONSOLE_COLOR_RED, "failed to initialize ACL");
25 + }
26 +
27 + // Reset the existing security permissions on each provided file.
28 + for (;;)
29 + {
30 + hr = ConsoleReadW(&sczFilePath);
31 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to read file path from stdin");
32 +
33 + if (!*sczFilePath)
34 + {
35 + break;
36 + }
37 +
38 + hr = ::SetNamedSecurityInfoW(sczFilePath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pacl, NULL);
39 + if (ERROR_FILE_NOT_FOUND != hr && ERROR_PATH_NOT_FOUND != hr)
40 + {
41 + ConsoleExitOnFailure(hr = HRESULT_FROM_WIN32(hr), CONSOLE_COLOR_RED, "failed to set security descriptor for file: %ls", sczFilePath);
42 + }
43 + }
44 +
45 + AssertSz(::IsValidAcl(pacl), "ResetAcls() - created invalid ACL");
46 +
47 +LExit:
48 + ReleaseStr(sczFilePath);
49 + ReleaseMem(pacl);
50 + return hr;
51 +}
src/wixnative/smartcab.cpp new
+157
@@ -0,0 +1,157 @@
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 +#include "precomp.h"
4 +
5 +static HRESULT CompressFiles(HANDLE hCab);
6 +static void __stdcall CabNamesCallback(LPWSTR wzFirstCabName, LPWSTR wzNewCabName, LPWSTR wzFileToken);
7 +
8 +
9 +HRESULT SmartCabCommand(
10 + __in int argc,
11 + __in LPWSTR argv[]
12 +)
13 +{
14 + HRESULT hr = E_INVALIDARG;
15 + LPCWSTR wzCabPath = NULL;
16 + LPCWSTR wzCabName = NULL;
17 + LPWSTR sczCabDir = NULL;
18 + UINT uiFileCount = 0;
19 + UINT uiMaxSize = 0;
20 + UINT uiMaxThresh = 0;
21 + COMPRESSION_TYPE ct = COMPRESSION_TYPE_NONE;
22 + HANDLE hCab = NULL;
23 +
24 + if (argc < 1)
25 + {
26 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Must specify: outCabPath [compressionType] [fileCount] [maxSizePerCabInMB [maxThreshold]]");
27 + }
28 + else
29 + {
30 + wzCabPath = argv[0];
31 + wzCabName = PathFile(wzCabPath);
32 +
33 + hr = PathGetDirectory(wzCabPath, &sczCabDir);
34 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse directory from path: %ls", wzCabPath);
35 +
36 + if (argc > 1)
37 + {
38 + UINT uiCompressionType;
39 + hr = StrStringToUInt32(argv[1], 0, &uiCompressionType);
40 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse compression type as number: %ls", argv[1]);
41 +
42 + ct = (uiCompressionType > 4) ? COMPRESSION_TYPE_HIGH : static_cast<COMPRESSION_TYPE>(uiCompressionType);
43 + }
44 +
45 + if (argc > 2)
46 + {
47 + hr = StrStringToUInt32(argv[2], 0, &uiFileCount);
48 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse file count as number: %ls", argv[2]);
49 + }
50 +
51 + if (argc > 3)
52 + {
53 + hr = StrStringToUInt32(argv[3], 0, &uiMaxSize);
54 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse max size as number: %ls", argv[3]);
55 + }
56 +
57 + if (argc > 4)
58 + {
59 + hr = StrStringToUInt32(argv[4], 0, &uiMaxThresh);
60 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "Could not parse max threshold as number: %ls", argv[4]);
61 + }
62 + }
63 +
64 + hr = CabCBegin(wzCabName, sczCabDir, uiFileCount, uiMaxSize, uiMaxThresh, ct, &hCab);
65 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to initialize cabinet: %ls", wzCabPath);
66 +
67 + hr = CompressFiles(hCab);
68 + ExitOnFailure(hr, "failed to compress files into cabinet: %ls", wzCabPath);
69 +
70 + hr = CabCFinish(hCab, CabNamesCallback);
71 + hCab = NULL; // once finish is called, the handle is invalid.
72 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to compress cabinet: %ls", wzCabPath);
73 +
74 +
75 +LExit:
76 + if (hCab)
77 + {
78 + CabCCancel(hCab);
79 + }
80 + ReleaseStr(sczCabDir);
81 +
82 + return hr;
83 +}
84 +
85 +
86 +static HRESULT CompressFiles(
87 + __in HANDLE hCab
88 +)
89 +{
90 + HRESULT hr = S_OK;
91 + LPWSTR sczLine = NULL;
92 + LPWSTR* rgsczSplit = NULL;
93 + UINT cSplit = 0;
94 + MSIFILEHASHINFO hashInfo = { sizeof(MSIFILEHASHINFO) };
95 +
96 + for (;;)
97 + {
98 + hr = ConsoleReadW(&sczLine);
99 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to read smartcab line from stdin");
100 +
101 + if (!*sczLine)
102 + {
103 + break;
104 + }
105 +
106 + hr = StrSplitAllocArray(&rgsczSplit, &cSplit, sczLine, L"\t");
107 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line from stdin: %ls", sczLine);
108 +
109 + if (cSplit != 2 && cSplit != 6)
110 + {
111 + hr = E_INVALIDARG;
112 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to split smartcab line into hash x 4, token, source file: %ls", sczLine);
113 + }
114 +
115 + LPCWSTR wzFilePath = rgsczSplit[0];
116 + LPCWSTR wzToken = rgsczSplit[1];
117 + PMSIFILEHASHINFO pHashInfo = NULL;
118 +
119 + if (cSplit == 6)
120 + {
121 + for (int i = 0; i < 4; ++i)
122 + {
123 + LPCWSTR wzHash = rgsczSplit[i + 2];
124 +
125 + hr = StrStringToInt32(wzHash, 0, reinterpret_cast<INT*>(hashInfo.dwData + i));
126 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to parse hash: %ls for file: %ls", wzHash, wzFilePath);
127 + }
128 +
129 + pHashInfo = &hashInfo;
130 + }
131 +
132 + hr = CabCAddFile(wzFilePath, wzToken, pHashInfo, hCab);
133 + ConsoleExitOnFailure(hr, CONSOLE_COLOR_RED, "failed to add file: %ls", wzFilePath);
134 +
135 + ReleaseNullStrArray(rgsczSplit, cSplit);
136 + }
137 +
138 +LExit:
139 + ReleaseNullStrArray(rgsczSplit, cSplit);
140 + ReleaseStr(sczLine);
141 +
142 + return hr;
143 +}
144 +
145 +
146 +// Callback from PFNFCIGETNEXTCABINET CabCGetNextCabinet method
147 +// First argument is the name of splitting cabinet without extension e.g. "cab1"
148 +// Second argument is name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"
149 +// Third argument is the file token of the first file present in the splitting cabinet
150 +static void __stdcall CabNamesCallback(
151 + __in LPWSTR wzFirstCabName,
152 + __in LPWSTR wzNewCabName,
153 + __in LPWSTR wzFileToken
154 +)
155 +{
156 + ConsoleWriteLine(CONSOLE_COLOR_NORMAL, "%ls\t%ls\t%ls", wzFirstCabName, wzNewCabName, wzFileToken);
157 +}
src/wixnative/wixnative.cpp new
+38
@@ -0,0 +1,38 @@
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 +#include "precomp.h"
4 +
5 +int __cdecl wmain(int argc, LPWSTR argv[])
6 +{
7 + HRESULT hr = E_INVALIDARG;
8 +
9 + ConsoleInitialize();
10 +
11 + if (argc < 2)
12 + {
13 + ConsoleWriteError(hr, CONSOLE_COLOR_RED, "Must specify a command");
14 + }
15 + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"smartcab", -1))
16 + {
17 + hr = SmartCabCommand(argc - 2, argv + 2);
18 + }
19 + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"extractcab", -1))
20 + {
21 + hr = ExtractCabCommand(argc - 2, argv + 2);
22 + }
23 + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"enumcab", -1))
24 + {
25 + hr = EnumCabCommand(argc - 2, argv + 2);
26 + }
27 + else if (CSTR_EQUAL == ::CompareString(LOCALE_INVARIANT, NORM_IGNORECASE, argv[1], -1, L"resetacls", -1))
28 + {
29 + hr = ResetAclsCommand(argc - 2, argv + 2);
30 + }
31 + else
32 + {
33 + ConsoleWriteError(hr, CONSOLE_COLOR_RED, "Unknown command: %ls", argv[1]);
34 + }
35 +
36 + ConsoleUninitialize();
37 + return HRESULT_CODE(hr);
38 +}
src/wixnative/wixnative.vcxproj new
+80
@@ -0,0 +1,80 @@
1 +<?xml version="1.0" encoding="utf-8"?>
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 +<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 + <Import Project="..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" />
6 +
7 + <ItemGroup Label="ProjectConfigurations">
8 + <ProjectConfiguration Include="Debug|Win32">
9 + <Configuration>Debug</Configuration>
10 + <Platform>Win32</Platform>
11 + </ProjectConfiguration>
12 + <ProjectConfiguration Include="Release|Win32">
13 + <Configuration>Release</Configuration>
14 + <Platform>Win32</Platform>
15 + </ProjectConfiguration>
16 + <ProjectConfiguration Include="Debug|x64">
17 + <Configuration>Debug</Configuration>
18 + <Platform>x64</Platform>
19 + </ProjectConfiguration>
20 + <ProjectConfiguration Include="Release|x64">
21 + <Configuration>Release</Configuration>
22 + <Platform>x64</Platform>
23 + </ProjectConfiguration>
24 + </ItemGroup>
25 +
26 + <PropertyGroup Label="Globals">
27 + <ProjectGuid>{8497EC72-B8D0-4272-A9D0-7E9D871CEFBF}</ProjectGuid>
28 + <ConfigurationType>Application</ConfigurationType>
29 + <ProjectSubSystem>Console</ProjectSubSystem>
30 + <TargetName>wixnative</TargetName>
31 + <PlatformToolset>v141</PlatformToolset>
32 + <CharacterSet>Unicode</CharacterSet>
33 + <Description>Native component of WixToolset.Core</Description>
34 + </PropertyGroup>
35 +
36 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
37 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
38 +
39 + <ImportGroup Label="ExtensionSettings">
40 + </ImportGroup>
41 +
42 + <ImportGroup Label="Shared">
43 + </ImportGroup>
44 +
45 + <PropertyGroup>
46 + <ProjectAdditionalLinkLibraries>crypt32.lib;cabinet.lib;msi.lib</ProjectAdditionalLinkLibraries>
47 + </PropertyGroup>
48 +
49 + <ItemGroup>
50 + <ClCompile Include="wixnative.cpp">
51 + <!-- turn off deprecation warning -->
52 + <DisableSpecificWarnings>4996</DisableSpecificWarnings>
53 + </ClCompile>
54 + <ClCompile Include="precomp.cpp">
55 + <PrecompiledHeader>Create</PrecompiledHeader>
56 + </ClCompile>
57 + <ClCompile Include="enumcab.cpp"/>
58 + <ClCompile Include="extractcab.cpp"/>
59 + <ClCompile Include="resetacls.cpp"/>
60 + <ClCompile Include="smartcab.cpp"/>
61 + </ItemGroup>
62 +
63 + <ItemGroup>
64 + <ClInclude Include="precomp.h" />
65 + </ItemGroup>
66 +
67 + <ItemGroup>
68 + <None Include="packages.config" />
69 + </ItemGroup>
70 +
71 + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
72 + <Import Project="..\..\packages\Nerdbank.GitVersioning.2.0.41\build\Nerdbank.GitVersioning.targets" Condition="Exists('..\..\packages\Nerdbank.GitVersioning.2.0.41\build\Nerdbank.GitVersioning.targets')" />
73 + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
74 + <PropertyGroup>
75 + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
76 + </PropertyGroup>
77 + <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.3\build\WixToolset.DUtil.props'))" />
78 + <Error Condition="!Exists('..\..\packages\Nerdbank.GitVersioning.2.0.41\build\Nerdbank.GitVersioning.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Nerdbank.GitVersioning.2.0.41\build\Nerdbank.GitVersioning.targets'))" />
79 + </Target>
80 +</Project>