diff options
author | Rob Mensching <rob@firegiant.com> | 2017-11-29 14:08:08 -0800 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2017-11-29 14:08:08 -0800 |
commit | ea3d18595a610ee07b03f07af4f03cf75b5ab420 (patch) | |
tree | b69ac2185b05254b136051d561b189c4fda1fc5b /src | |
parent | 95f4f9b9b99e1a6f91f4687c2dd511a6d6fc2716 (diff) | |
download | wix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.tar.gz wix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.tar.bz2 wix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.zip |
Improved cabinet handling
Diffstat (limited to 'src')
23 files changed, 1237 insertions, 4 deletions
diff --git a/src/WixToolset.Core.Native/Cabinet.cs b/src/WixToolset.Core.Native/Cabinet.cs new file mode 100644 index 00000000..27b0ec74 --- /dev/null +++ b/src/WixToolset.Core.Native/Cabinet.cs | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core.Native/CabinetCompressFile.cs b/src/WixToolset.Core.Native/CabinetCompressFile.cs new file mode 100644 index 00000000..6778f4a1 --- /dev/null +++ b/src/WixToolset.Core.Native/CabinetCompressFile.cs | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core.Native/CabinetCompressionLevel.cs b/src/WixToolset.Core.Native/CabinetCompressionLevel.cs new file mode 100644 index 00000000..fce1ff41 --- /dev/null +++ b/src/WixToolset.Core.Native/CabinetCompressionLevel.cs | |||
@@ -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 | ||
diff --git a/src/WixToolset.Core.Native/CabinetFileInfo.cs b/src/WixToolset.Core.Native/CabinetFileInfo.cs new file mode 100644 index 00000000..ea229121 --- /dev/null +++ b/src/WixToolset.Core.Native/CabinetFileInfo.cs | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core.Native/WixNativeExe.cs b/src/WixToolset.Core.Native/WixNativeExe.cs new file mode 100644 index 00000000..8626bea3 --- /dev/null +++ b/src/WixToolset.Core.Native/WixNativeExe.cs | |||
@@ -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 | } | ||
diff --git a/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj b/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj index 3e66d84e..aa87186b 100644 --- a/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj +++ b/src/WixToolset.Core.Native/WixToolset.Core.Native.csproj | |||
@@ -3,6 +3,7 @@ | |||
3 | <PropertyGroup> | 3 | <PropertyGroup> |
4 | <TargetFramework>netstandard2.0</TargetFramework> | 4 | <TargetFramework>netstandard2.0</TargetFramework> |
5 | <NuspecFile>$(MSBuildThisFileName).nuspec</NuspecFile> | 5 | <NuspecFile>$(MSBuildThisFileName).nuspec</NuspecFile> |
6 | <Description>Core Native</Description> | ||
6 | <!-- <BeforePack>SetNuspecProperties</BeforePack> --> | 7 | <!-- <BeforePack>SetNuspecProperties</BeforePack> --> |
7 | </PropertyGroup> | 8 | </PropertyGroup> |
8 | 9 | ||
@@ -10,9 +11,26 @@ | |||
10 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" /> | 11 | <PackageReference Include="Nerdbank.GitVersioning" Version="2.0.41" PrivateAssets="all" /> |
11 | </ItemGroup> | 12 | </ItemGroup> |
12 | 13 | ||
13 | <!-- <ItemGroup> | 14 | <Target Name="BuildWixNative" |
14 | <ProjectReference Include="..\winterop\winterop.vcxproj" ExcludeAssets="All" /> | 15 | BeforeTargets="GetCopyToOutputDirectoryItems"> |
15 | </ItemGroup> --> | 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 | |||
16 | <Target Name="SetNuspecProperties" | 34 | <Target Name="SetNuspecProperties" |
17 | AfterTargets="Build"> | 35 | AfterTargets="Build"> |
18 | <PropertyGroup> | 36 | <PropertyGroup> |
diff --git a/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec b/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec index 68d154c8..6a96167e 100644 --- a/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec +++ b/src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec | |||
@@ -10,15 +10,20 @@ | |||
10 | <requireLicenseAcceptance>false</requireLicenseAcceptance> | 10 | <requireLicenseAcceptance>false</requireLicenseAcceptance> |
11 | <description>$description$</description> | 11 | <description>$description$</description> |
12 | <copyright>$copyright$</copyright> | 12 | <copyright>$copyright$</copyright> |
13 | 13 | <!-- | |
14 | <dependencies> | 14 | <dependencies> |
15 | <dependency id="runtime.win-x86.WixToolset.Core.Native" version="$version$" /> | 15 | <dependency id="runtime.win-x86.WixToolset.Core.Native" version="$version$" /> |
16 | <dependency id="runtime.win-x64.WixToolset.Core.Native" version="$version$" /> | 16 | <dependency id="runtime.win-x64.WixToolset.Core.Native" version="$version$" /> |
17 | </dependencies> | 17 | </dependencies> |
18 | --> | ||
18 | </metadata> | 19 | </metadata> |
19 | 20 | ||
20 | <files> | 21 | <files> |
21 | <file src="$id$.dll" target="lib\netstandard2.0" /> | 22 | <file src="$id$.dll" target="lib\netstandard2.0" /> |
22 | <file src="$id$.pdb" 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" /> | ||
23 | </files> | 28 | </files> |
24 | </package> | 29 | </package> |
diff --git a/src/test/WixToolsetTest.Core.Native/CabinetFixture.cs b/src/test/WixToolsetTest.Core.Native/CabinetFixture.cs new file mode 100644 index 00000000..baab3bee --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/CabinetFixture.cs | |||
@@ -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 | } | ||
diff --git a/src/test/WixToolsetTest.Core.Native/TestData/test.cab b/src/test/WixToolsetTest.Core.Native/TestData/test.cab new file mode 100644 index 00000000..ca78f632 --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/TestData/test.cab | |||
Binary files differ | |||
diff --git a/src/test/WixToolsetTest.Core.Native/TestData/test.txt b/src/test/WixToolsetTest.Core.Native/TestData/test.txt new file mode 100644 index 00000000..cd0db0e1 --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/TestData/test.txt | |||
@@ -0,0 +1 @@ | |||
This is test.txt. \ No newline at end of file | |||
diff --git a/src/test/WixToolsetTest.Core.Native/Utility/DisposableFileSystem.cs b/src/test/WixToolsetTest.Core.Native/Utility/DisposableFileSystem.cs new file mode 100644 index 00000000..c9957247 --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/Utility/DisposableFileSystem.cs | |||
@@ -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 | } | ||
diff --git a/src/test/WixToolsetTest.Core.Native/Utility/Pushd.cs b/src/test/WixToolsetTest.Core.Native/Utility/Pushd.cs new file mode 100644 index 00000000..91700c2f --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/Utility/Pushd.cs | |||
@@ -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 | } | ||
diff --git a/src/test/WixToolsetTest.Core.Native/Utility/TestData.cs b/src/test/WixToolsetTest.Core.Native/Utility/TestData.cs new file mode 100644 index 00000000..cd9c6318 --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/Utility/TestData.cs | |||
@@ -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 | } | ||
diff --git a/src/test/WixToolsetTest.Core.Native/WixToolsetTest.Core.Native.csproj b/src/test/WixToolsetTest.Core.Native/WixToolsetTest.Core.Native.csproj new file mode 100644 index 00000000..c7fd89ea --- /dev/null +++ b/src/test/WixToolsetTest.Core.Native/WixToolsetTest.Core.Native.csproj | |||
@@ -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> | ||
diff --git a/src/wixnative/enumcab.cpp b/src/wixnative/enumcab.cpp new file mode 100644 index 00000000..e7717bac --- /dev/null +++ b/src/wixnative/enumcab.cpp | |||
@@ -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 | } | ||
diff --git a/src/wixnative/extractcab.cpp b/src/wixnative/extractcab.cpp new file mode 100644 index 00000000..53f53266 --- /dev/null +++ b/src/wixnative/extractcab.cpp | |||
@@ -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 | } | ||
diff --git a/src/wixnative/packages.config b/src/wixnative/packages.config new file mode 100644 index 00000000..02ee2250 --- /dev/null +++ b/src/wixnative/packages.config | |||
@@ -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 | ||
diff --git a/src/wixnative/precomp.cpp b/src/wixnative/precomp.cpp new file mode 100644 index 00000000..37664a1c --- /dev/null +++ b/src/wixnative/precomp.cpp | |||
@@ -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" | ||
diff --git a/src/wixnative/precomp.h b/src/wixnative/precomp.h new file mode 100644 index 00000000..5bd617e5 --- /dev/null +++ b/src/wixnative/precomp.h | |||
@@ -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[]); | ||
diff --git a/src/wixnative/resetacls.cpp b/src/wixnative/resetacls.cpp new file mode 100644 index 00000000..8c5bdc56 --- /dev/null +++ b/src/wixnative/resetacls.cpp | |||
@@ -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 | } | ||
diff --git a/src/wixnative/smartcab.cpp b/src/wixnative/smartcab.cpp new file mode 100644 index 00000000..da9087a3 --- /dev/null +++ b/src/wixnative/smartcab.cpp | |||
@@ -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 | } | ||
diff --git a/src/wixnative/wixnative.cpp b/src/wixnative/wixnative.cpp new file mode 100644 index 00000000..7bd8dbca --- /dev/null +++ b/src/wixnative/wixnative.cpp | |||
@@ -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 | } | ||
diff --git a/src/wixnative/wixnative.vcxproj b/src/wixnative/wixnative.vcxproj new file mode 100644 index 00000000..2a4ce3d5 --- /dev/null +++ b/src/wixnative/wixnative.vcxproj | |||
@@ -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> | ||