aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-11-29 14:08:08 -0800
committerRob Mensching <rob@firegiant.com>2017-11-29 14:08:08 -0800
commitea3d18595a610ee07b03f07af4f03cf75b5ab420 (patch)
treeb69ac2185b05254b136051d561b189c4fda1fc5b /src
parent95f4f9b9b99e1a6f91f4687c2dd511a6d6fc2716 (diff)
downloadwix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.tar.gz
wix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.tar.bz2
wix-ea3d18595a610ee07b03f07af4f03cf75b5ab420.zip
Improved cabinet handling
Diffstat (limited to 'src')
-rw-r--r--src/WixToolset.Core.Native/Cabinet.cs199
-rw-r--r--src/WixToolset.Core.Native/CabinetCompressFile.cs65
-rw-r--r--src/WixToolset.Core.Native/CabinetCompressionLevel.cs25
-rw-r--r--src/WixToolset.Core.Native/CabinetFileInfo.cs67
-rw-r--r--src/WixToolset.Core.Native/WixNativeExe.cs115
-rw-r--r--src/WixToolset.Core.Native/WixToolset.Core.Native.csproj24
-rw-r--r--src/WixToolset.Core.Native/WixToolset.Core.Native.nuspec7
-rw-r--r--src/test/WixToolsetTest.Core.Native/CabinetFixture.cs115
-rw-r--r--src/test/WixToolsetTest.Core.Native/TestData/test.cabbin0 -> 115 bytes
-rw-r--r--src/test/WixToolsetTest.Core.Native/TestData/test.txt1
-rw-r--r--src/test/WixToolsetTest.Core.Native/Utility/DisposableFileSystem.cs86
-rw-r--r--src/test/WixToolsetTest.Core.Native/Utility/Pushd.cs46
-rw-r--r--src/test/WixToolsetTest.Core.Native/Utility/TestData.cs17
-rw-r--r--src/test/WixToolsetTest.Core.Native/WixToolsetTest.Core.Native.csproj24
-rw-r--r--src/wixnative/enumcab.cpp47
-rw-r--r--src/wixnative/extractcab.cpp50
-rw-r--r--src/wixnative/packages.config5
-rw-r--r--src/wixnative/precomp.cpp3
-rw-r--r--src/wixnative/precomp.h19
-rw-r--r--src/wixnative/resetacls.cpp51
-rw-r--r--src/wixnative/smartcab.cpp157
-rw-r--r--src/wixnative/wixnative.cpp38
-rw-r--r--src/wixnative/wixnative.vcxproj80
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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
3namespace 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
5static INT_PTR __stdcall EnumCallback(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin);
6
7
8HRESULT 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
29LExit:
30 CabUninitialize();
31
32 return hr;
33}
34
35
36static 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
5static HRESULT ProgressCallback(BOOL fBeginFile, LPCWSTR wzFileId, LPVOID pvContext);
6
7
8HRESULT 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
31LExit:
32 CabUninitialize();
33
34 return hr;
35}
36
37
38static 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
16HRESULT SmartCabCommand(int argc, LPWSTR argv[]);
17HRESULT ResetAclsCommand(int argc, LPWSTR argv[]);
18HRESULT EnumCabCommand(int argc, LPWSTR argv[]);
19HRESULT 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
5HRESULT 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
47LExit:
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
5static HRESULT CompressFiles(HANDLE hCab);
6static void __stdcall CabNamesCallback(LPWSTR wzFirstCabName, LPWSTR wzNewCabName, LPWSTR wzFileToken);
7
8
9HRESULT 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
75LExit:
76 if (hCab)
77 {
78 CabCCancel(hCab);
79 }
80 ReleaseStr(sczCabDir);
81
82 return hr;
83}
84
85
86static 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
138LExit:
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
150static 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
5int __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>