aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-03-16 10:49:09 -0700
committerRob Mensching <rob@firegiant.com>2021-03-16 11:07:44 -0700
commit60f75abcd1fe49052c118a2597ac59a82c372b64 (patch)
tree1fd88e6c67846b97e61dbc3bf6f5f440516829a2 /src
parent1c23520ed490b56e292dc1544463af83807745ad (diff)
downloadwix-60f75abcd1fe49052c118a2597ac59a82c372b64.tar.gz
wix-60f75abcd1fe49052c118a2597ac59a82c372b64.tar.bz2
wix-60f75abcd1fe49052c118a2597ac59a82c372b64.zip
Migrate PInvoke out of Core to Core.Native
Diffstat (limited to 'src')
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnCommon.cs25
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnReader.cs21
-rw-r--r--src/WixToolset.Core.Burn/Bundles/BurnWriter.cs2
-rw-r--r--src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs3
-rw-r--r--src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs21
-rw-r--r--src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs34
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs92
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs10
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs7
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs4
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs508
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Database.cs248
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs363
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs77
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs117
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/MsiInterop.cs571
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Record.cs181
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/Session.cs43
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs244
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/View.cs262
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Msi/WixInvalidIdtException.cs33
-rw-r--r--src/WixToolset.Core.WindowsInstaller/MspBackend.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs437
-rw-r--r--src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs989
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs6
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs2
-rw-r--r--src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs288
-rw-r--r--src/WixToolset.Core/Bind/FileSystem.cs86
-rw-r--r--src/WixToolset.Core/Bind/TransferFilesCommand.cs52
-rw-r--r--src/WixToolset.Core/Inscriber.cs437
-rw-r--r--src/WixToolset.Core/WixToolset.Core.csproj1
42 files changed, 104 insertions, 5086 deletions
diff --git a/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index bca1be72..ab3b7896 100644
--- a/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -111,7 +111,7 @@ namespace WixToolset.Core.Burn.Bundles
111 /// <param name="fileExe">File to modify in-place.</param> 111 /// <param name="fileExe">File to modify in-place.</param>
112 public BurnCommon(IMessaging messaging, string fileExe) 112 public BurnCommon(IMessaging messaging, string fileExe)
113 { 113 {
114 this.messaging = messaging; 114 this.Messaging = messaging;
115 this.fileExe = fileExe; 115 this.fileExe = fileExe;
116 } 116 }
117 117
@@ -130,7 +130,7 @@ namespace WixToolset.Core.Burn.Bundles
130 public UInt32 AttachedContainerAddress { get; protected set; } 130 public UInt32 AttachedContainerAddress { get; protected set; }
131 public UInt32 AttachedContainerSize { get; protected set; } 131 public UInt32 AttachedContainerSize { get; protected set; }
132 132
133 protected IMessaging messaging { get; } 133 protected IMessaging Messaging { get; }
134 134
135 public void Dispose() 135 public void Dispose()
136 { 136 {
@@ -147,12 +147,11 @@ namespace WixToolset.Core.Burn.Bundles
147 /// <param name="size">Optional count of bytes to copy. 0 indicates whole input stream from current should be copied.</param> 147 /// <param name="size">Optional count of bytes to copy. 0 indicates whole input stream from current should be copied.</param>
148 protected static int CopyStream(Stream input, Stream output, int size) 148 protected static int CopyStream(Stream input, Stream output, int size)
149 { 149 {
150 byte[] bytes = new byte[4096]; 150 var bytes = new byte[4096];
151 int total = 0; 151 var total = 0;
152 int read = 0;
153 do 152 do
154 { 153 {
155 read = Math.Min(bytes.Length, size - total); 154 var read = Math.Min(bytes.Length, size - total);
156 read = input.Read(bytes, 0, read); 155 read = input.Read(bytes, 0, read);
157 if (0 == read) 156 if (0 == read)
158 { 157 {
@@ -185,21 +184,21 @@ namespace WixToolset.Core.Burn.Bundles
185 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); 184 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC);
186 if (BURN_SECTION_MAGIC != uint32) 185 if (BURN_SECTION_MAGIC != uint32)
187 { 186 {
188 this.messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); 187 this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe));
189 return false; 188 return false;
190 } 189 }
191 190
192 this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION); 191 this.Version = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_VERSION);
193 if (BURN_SECTION_VERSION != this.Version) 192 if (BURN_SECTION_VERSION != this.Version)
194 { 193 {
195 this.messaging.Write(ErrorMessages.BundleTooNew(this.fileExe, this.Version)); 194 this.Messaging.Write(ErrorMessages.BundleTooNew(this.fileExe, this.Version));
196 return false; 195 return false;
197 } 196 }
198 197
199 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now 198 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_FORMAT); // We only know how to deal with CABs right now
200 if (1 != uint32) 199 if (1 != uint32)
201 { 200 {
202 this.messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); 201 this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe));
203 return false; 202 return false;
204 } 203 }
205 204
@@ -266,7 +265,7 @@ namespace WixToolset.Core.Burn.Bundles
266 265
267 if (UInt32.MaxValue == wixburnSectionOffset) 266 if (UInt32.MaxValue == wixburnSectionOffset)
268 { 267 {
269 this.messaging.Write(ErrorMessages.StubMissingWixburnSection(this.fileExe)); 268 this.Messaging.Write(ErrorMessages.StubMissingWixburnSection(this.fileExe));
270 return false; 269 return false;
271 } 270 }
272 271
@@ -274,7 +273,7 @@ namespace WixToolset.Core.Burn.Bundles
274 // the smallest alignment (512 bytes), but just to be paranoid... 273 // the smallest alignment (512 bytes), but just to be paranoid...
275 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) 274 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA))
276 { 275 {
277 this.messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe)); 276 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe));
278 return false; 277 return false;
279 } 278 }
280 279
@@ -303,7 +302,7 @@ namespace WixToolset.Core.Burn.Bundles
303 // Verify the NT signature... 302 // Verify the NT signature...
304 if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE)) 303 if (IMAGE_NT_SIGNATURE != BurnCommon.ReadUInt32(bytes, IMAGE_NT_HEADER_OFFSET_SIGNATURE))
305 { 304 {
306 this.messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); 305 this.Messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe));
307 return false; 306 return false;
308 } 307 }
309 308
@@ -338,7 +337,7 @@ namespace WixToolset.Core.Burn.Bundles
338 // Verify the DOS 'MZ' signature. 337 // Verify the DOS 'MZ' signature.
339 if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC)) 338 if (IMAGE_DOS_SIGNATURE != BurnCommon.ReadUInt16(bytes, IMAGE_DOS_HEADER_OFFSET_MAGIC))
340 { 339 {
341 this.messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe)); 340 this.Messaging.Write(ErrorMessages.InvalidStubExe(this.fileExe));
342 return false; 341 return false;
343 } 342 }
344 343
diff --git a/src/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/WixToolset.Core.Burn/Bundles/BurnReader.cs
index 68fdea1c..5b06b31e 100644
--- a/src/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -27,7 +27,7 @@ namespace WixToolset.Core.Burn.Bundles
27 27
28 private bool invalidBundle; 28 private bool invalidBundle;
29 private BinaryReader binaryReader; 29 private BinaryReader binaryReader;
30 private List<DictionaryEntry> attachedContainerPayloadNames; 30 private readonly List<DictionaryEntry> attachedContainerPayloadNames;
31 31
32 /// <summary> 32 /// <summary>
33 /// Creates a BurnReader for reading a PE file. 33 /// Creates a BurnReader for reading a PE file.
@@ -43,13 +43,7 @@ namespace WixToolset.Core.Burn.Bundles
43 /// <summary> 43 /// <summary>
44 /// Gets the underlying stream. 44 /// Gets the underlying stream.
45 /// </summary> 45 /// </summary>
46 public Stream Stream 46 public Stream Stream => this.binaryReader?.BaseStream;
47 {
48 get
49 {
50 return (null != this.binaryReader) ? this.binaryReader.BaseStream : null;
51 }
52 }
53 47
54 internal static BurnReader Open(object inputFilePath) 48 internal static BurnReader Open(object inputFilePath)
55 { 49 {
@@ -64,7 +58,7 @@ namespace WixToolset.Core.Burn.Bundles
64 /// <returns>Burn reader.</returns> 58 /// <returns>Burn reader.</returns>
65 public static BurnReader Open(IMessaging messaging, string fileExe) 59 public static BurnReader Open(IMessaging messaging, string fileExe)
66 { 60 {
67 BurnReader reader = new BurnReader(messaging, fileExe); 61 var reader = new BurnReader(messaging, fileExe);
68 62
69 reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); 63 reader.binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
70 if (!reader.Initialize(reader.binaryReader)) 64 if (!reader.Initialize(reader.binaryReader))
@@ -109,8 +103,7 @@ namespace WixToolset.Core.Burn.Bundles
109 cabinet.Extract(outputDirectory); 103 cabinet.Extract(outputDirectory);
110 104
111 Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)); 105 Directory.CreateDirectory(Path.GetDirectoryName(manifestPath));
112 File.Delete(manifestPath); 106 FileSystem.MoveFile(manifestOriginalPath, manifestPath);
113 File.Move(manifestOriginalPath, manifestPath);
114 107
115 XmlDocument document = new XmlDocument(); 108 XmlDocument document = new XmlDocument();
116 document.Load(manifestPath); 109 document.Load(manifestPath);
@@ -128,8 +121,7 @@ namespace WixToolset.Core.Burn.Bundles
128 string destinationPath = Path.Combine(outputDirectory, filePathNode.Value); 121 string destinationPath = Path.Combine(outputDirectory, filePathNode.Value);
129 122
130 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); 123 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
131 File.Delete(destinationPath); 124 FileSystem.MoveFile(sourcePath, destinationPath);
132 File.Move(sourcePath, destinationPath);
133 } 125 }
134 126
135 foreach (XmlNode payload in payloads) 127 foreach (XmlNode payload in payloads)
@@ -193,8 +185,7 @@ namespace WixToolset.Core.Burn.Bundles
193 string destinationPath = Path.Combine(outputDirectory, (string)entry.Value); 185 string destinationPath = Path.Combine(outputDirectory, (string)entry.Value);
194 186
195 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); 187 Directory.CreateDirectory(Path.GetDirectoryName(destinationPath));
196 File.Delete(destinationPath); 188 FileSystem.MoveFile(sourcePath, destinationPath);
197 File.Move(sourcePath, destinationPath);
198 } 189 }
199 190
200 return true; 191 return true;
diff --git a/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index be9b8eaa..2d16d11c 100644
--- a/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -80,7 +80,7 @@ namespace WixToolset.Core.Burn.Bundles
80 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC); 80 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_MAGIC, BURN_SECTION_MAGIC);
81 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION); 81 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_VERSION, BURN_SECTION_VERSION);
82 82
83 this.messaging.Write(VerboseMessages.BundleGuid(bundleId)); 83 this.Messaging.Write(VerboseMessages.BundleGuid(bundleId));
84 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin); 84 this.binaryWriter.BaseStream.Seek(this.wixburnDataOffset + BURN_SECTION_OFFSET_BUNDLEGUID, SeekOrigin.Begin);
85 this.binaryWriter.Write(bundleGuid.ToByteArray()); 85 this.binaryWriter.Write(bundleGuid.ToByteArray());
86 86
diff --git a/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index 66257ce3..b802f556 100644
--- a/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -8,6 +8,7 @@ namespace WixToolset.Core.Burn.Bundles
8 using System.Reflection; 8 using System.Reflection;
9 using System.Text; 9 using System.Text;
10 using System.Xml; 10 using System.Xml;
11 using WixToolset.Core.Native;
11 using WixToolset.Data; 12 using WixToolset.Data;
12 using WixToolset.Data.Burn; 13 using WixToolset.Data.Burn;
13 using WixToolset.Data.Symbols; 14 using WixToolset.Data.Symbols;
@@ -72,7 +73,7 @@ namespace WixToolset.Core.Burn.Bundles
72 73
73 this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers); 74 this.Transfer = this.BackendHelper.CreateFileTransfer(bundleTempPath, this.OutputPath, true, this.BundleSymbol.SourceLineNumbers);
74 75
75 File.Copy(stubFile, bundleTempPath, true); 76 FileSystem.CopyFile(stubFile, bundleTempPath, allowHardlink: false);
76 File.SetAttributes(bundleTempPath, FileAttributes.Normal); 77 File.SetAttributes(bundleTempPath, FileAttributes.Normal);
77 78
78 var windowsAssemblyVersion = GetWindowsAssemblyVersion(this.BundleSymbol); 79 var windowsAssemblyVersion = GetWindowsAssemblyVersion(this.BundleSymbol);
diff --git a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
index e87f4360..b466d0de 100644
--- a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+++ b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -1,10 +1,10 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. 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 2
3namespace WixToolset.Core.Burn.Inscribe 3namespace WixToolset.Core.Burn.Inscribe
4{ 4{
5 using System.IO; 5 using System.IO;
6 using WixToolset.Core.Burn.Bundles; 6 using WixToolset.Core.Burn.Bundles;
7 using WixToolset.Extensibility; 7 using WixToolset.Core.Native;
8 using WixToolset.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
10 10
@@ -23,19 +23,19 @@ namespace WixToolset.Core.Burn.Inscribe
23 23
24 public bool Execute() 24 public bool Execute()
25 { 25 {
26 bool inscribed = false; 26 var inscribed = false;
27 string tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_signed.exe"); 27 var tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_signed.exe");
28 28
29 using (BurnReader reader = BurnReader.Open(this.Context.InputFilePath)) 29 using (var reader = BurnReader.Open(this.Context.InputFilePath))
30 { 30 {
31 File.Copy(this.Context.SignedEngineFile, tempFile, true); 31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false);
32 32
33 // If there was an attached container on the original (unsigned) bundle, put it back. 33 // If there was an attached container on the original (unsigned) bundle, put it back.
34 if (reader.AttachedContainerSize > 0) 34 if (reader.AttachedContainerSize > 0)
35 { 35 {
36 reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin); 36 reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
37 37
38 using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile)) 38 using (var writer = BurnWriter.Open(this.Messaging, tempFile))
39 { 39 {
40 writer.RememberThenResetSignature(); 40 writer.RememberThenResetSignature();
41 writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached); 41 writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
@@ -45,13 +45,8 @@ namespace WixToolset.Core.Burn.Inscribe
45 } 45 }
46 46
47 Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile)); 47 Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile));
48 if (File.Exists(this.Context.OutputFile))
49 {
50 File.Delete(this.Context.OutputFile);
51 }
52 48
53 File.Move(tempFile, this.Context.OutputFile); 49 FileSystem.MoveFile(tempFile, this.Context.OutputFile);
54 WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { this.Context.OutputFile }, 1);
55 50
56 return inscribed; 51 return inscribed;
57 } 52 }
diff --git a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
index 37f64312..a6789796 100644
--- a/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+++ b/src/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
@@ -1,33 +1,40 @@
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. 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 2
3namespace WixToolset.Core.Burn.Inscribe 3namespace WixToolset.Core.Burn.Inscribe
4{ 4{
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
7 using WixToolset.Core.Burn.Bundles; 7 using WixToolset.Core.Burn.Bundles;
8 using WixToolset.Core.Native;
8 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
9 10
10 internal class InscribeBundleEngineCommand 11 internal class InscribeBundleEngineCommand
11 { 12 {
12 public InscribeBundleEngineCommand(IInscribeContext context) 13 public InscribeBundleEngineCommand(IInscribeContext context)
13 { 14 {
14 this.Context = context; 15 this.IntermediateFolder = context.IntermediateFolder;
16 this.InputFilePath = context.InputFilePath;
17 this.OutputFile = context.OutputFile;
15 } 18 }
16 19
17 private IInscribeContext Context { get; } 20 private string IntermediateFolder { get; }
21
22 private string InputFilePath { get; }
23
24 private string OutputFile { get; }
18 25
19 public bool Execute() 26 public bool Execute()
20 { 27 {
21 string tempFile = Path.Combine(this.Context.IntermediateFolder, "bundle_engine_unsigned.exe"); 28 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
22 29
23 using (BurnReader reader = BurnReader.Open(this.Context.InputFilePath)) 30 using (var reader = BurnReader.Open(this.InputFilePath))
24 using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete)) 31 using (var writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
25 { 32 {
26 reader.Stream.Seek(0, SeekOrigin.Begin); 33 reader.Stream.Seek(0, SeekOrigin.Begin);
27 34
28 byte[] buffer = new byte[4 * 1024]; 35 var buffer = new byte[4 * 1024];
29 int total = 0; 36 var total = 0;
30 int read = 0; 37 var read = 0;
31 do 38 do
32 { 39 {
33 read = Math.Min(buffer.Length, (int)reader.EngineSize - total); 40 read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
@@ -46,14 +53,9 @@ namespace WixToolset.Core.Burn.Inscribe
46 // TODO: update writer with detached container signatures. 53 // TODO: update writer with detached container signatures.
47 } 54 }
48 55
49 Directory.CreateDirectory(Path.GetDirectoryName(this.Context.OutputFile)); 56 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputFile));
50 if (File.Exists(this.Context.OutputFile))
51 {
52 File.Delete(this.Context.OutputFile);
53 }
54 57
55 File.Move(tempFile, this.Context.OutputFile); 58 FileSystem.MoveFile(tempFile, this.OutputFile);
56 WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { this.Context.OutputFile }, 1);
57 59
58 return true; 60 return true;
59 } 61 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
index b2052b90..6d802d98 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/AttachPatchTransformsCommand.cs
@@ -7,7 +7,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
7 using System.Globalization; 7 using System.Globalization;
8 using System.Linq; 8 using System.Linq;
9 using System.Text.RegularExpressions; 9 using System.Text.RegularExpressions;
10 using WixToolset.Core.WindowsInstaller.Msi; 10 using WixToolset.Core.Native.Msi;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Symbols; 12 using WixToolset.Data.Symbols;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index 28e1d9ee..3a9bd545 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -5,7 +5,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System; 5 using System;
6 using System.Globalization; 6 using System.Globalization;
7 using System.IO; 7 using System.IO;
8 using WixToolset.Core.WindowsInstaller.Msi; 8 using WixToolset.Core.Native.Msi;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
11 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
deleted file mode 100644
index 9a609463..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ConfigurationCallback.cs
+++ /dev/null
@@ -1,92 +0,0 @@
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.WindowsInstaller.Bind
4{
5 using System;
6 using System.Collections;
7 using System.Globalization;
8 using WixToolset.Core.Native;
9 using WixToolset.Core.WindowsInstaller.Msi;
10
11 /// <summary>
12 /// Callback object for configurable merge modules.
13 /// </summary>
14 internal sealed class ConfigurationCallback : IMsmConfigureModule
15 {
16 private const int SOk = 0x0;
17 private const int SFalse = 0x1;
18 private Hashtable configurationData;
19
20 /// <summary>
21 /// Creates a ConfigurationCallback object.
22 /// </summary>
23 /// <param name="configData">String to break up into name/value pairs.</param>
24 public ConfigurationCallback(string configData)
25 {
26 if (String.IsNullOrEmpty(configData))
27 {
28 throw new ArgumentNullException("configData");
29 }
30
31 string[] pairs = configData.Split(',');
32 this.configurationData = new Hashtable(pairs.Length);
33 for (int i = 0; i < pairs.Length; ++i)
34 {
35 string[] nameVal = pairs[i].Split('=');
36 string name = nameVal[0];
37 string value = nameVal[1];
38
39 name = name.Replace("%2C", ",");
40 name = name.Replace("%3D", "=");
41 name = name.Replace("%25", "%");
42
43 value = value.Replace("%2C", ",");
44 value = value.Replace("%3D", "=");
45 value = value.Replace("%25", "%");
46
47 this.configurationData[name] = value;
48 }
49 }
50
51 /// <summary>
52 /// Returns text data based on name.
53 /// </summary>
54 /// <param name="name">Name of value to return.</param>
55 /// <param name="configData">Out param to put configuration data into.</param>
56 /// <returns>S_OK if value provided, S_FALSE if not.</returns>
57 public int ProvideTextData(string name, out string configData)
58 {
59 if (this.configurationData.Contains(name))
60 {
61 configData = (string)this.configurationData[name];
62 return SOk;
63 }
64 else
65 {
66 configData = null;
67 return SFalse;
68 }
69 }
70
71 /// <summary>
72 /// Returns integer data based on name.
73 /// </summary>
74 /// <param name="name">Name of value to return.</param>
75 /// <param name="configData">Out param to put configuration data into.</param>
76 /// <returns>S_OK if value provided, S_FALSE if not.</returns>
77 public int ProvideIntegerData(string name, out int configData)
78 {
79 if (this.configurationData.Contains(name))
80 {
81 string val = (string)this.configurationData[name];
82 configData = Convert.ToInt32(val, CultureInfo.InvariantCulture);
83 return SOk;
84 }
85 else
86 {
87 configData = 0;
88 return SFalse;
89 }
90 }
91 }
92}
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
index 9a631754..d0e25571 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateInstanceTransformsCommand.cs
@@ -5,7 +5,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq; 7 using System.Linq;
8 using WixToolset.Core.WindowsInstaller.Msi; 8 using WixToolset.Core.Native.Msi;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
11 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
index 7bc1a8bd..5c993f63 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
@@ -6,7 +6,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.WindowsInstaller.Msi; 9 using WixToolset.Core.Native.Msi;
10 using WixToolset.Core.WindowsInstaller.Unbind; 10 using WixToolset.Core.WindowsInstaller.Unbind;
11 using WixToolset.Data; 11 using WixToolset.Data;
12 using WixToolset.Data.Symbols; 12 using WixToolset.Data.Symbols;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
index d4de2dd3..7c1e085c 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ExtractMergeModuleFilesCommand.cs
@@ -9,12 +9,13 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using System.Runtime.InteropServices; 11 using System.Runtime.InteropServices;
12 using WixToolset.Data;
13 using WixToolset.Core.Native; 12 using WixToolset.Core.Native;
13 using WixToolset.Core.Native.Msi;
14 using WixToolset.Core.Native.Msm;
15 using WixToolset.Data;
14 using WixToolset.Data.Symbols; 16 using WixToolset.Data.Symbols;
15 using WixToolset.Extensibility.Services;
16 using WixToolset.Core.WindowsInstaller.Msi;
17 using WixToolset.Extensibility.Data; 17 using WixToolset.Extensibility.Data;
18 using WixToolset.Extensibility.Services;
18 19
19 /// <summary> 20 /// <summary>
20 /// Retrieve files information and extract them from merge modules. 21 /// Retrieve files information and extract them from merge modules.
@@ -52,8 +53,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
52 { 53 {
53 var mergeModulesFileFacades = new List<IFileFacade>(); 54 var mergeModulesFileFacades = new List<IFileFacade>();
54 55
55 var interop = new MsmInterop(); 56 var merge = MsmInterop.GetMsmMerge();
56 var merge = interop.GetMsmMerge();
57 57
58 // Index all of the file rows to be able to detect collisions with files in the Merge Modules. 58 // Index all of the file rows to be able to detect collisions with files in the Merge Modules.
59 // It may seem a bit expensive to build up this index solely for the purpose of checking collisions 59 // It may seem a bit expensive to build up this index solely for the purpose of checking collisions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index d3c65b6a..06fbf072 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -8,7 +8,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
8 using System.IO; 8 using System.IO;
9 using System.Linq; 9 using System.Linq;
10 using System.Text; 10 using System.Text;
11 using WixToolset.Core.WindowsInstaller.Msi; 11 using WixToolset.Core.Native.Msi;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility.Data; 14 using WixToolset.Extensibility.Data;
@@ -337,7 +337,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
337 } 337 }
338 338
339 // check for a stream name that is more than 62 characters long (the maximum allowed length) 339 // check for a stream name that is more than 62 characters long (the maximum allowed length)
340 if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length) 340 if (needStream && Database.MsiMaxStreamNameLength < streamName.Length)
341 { 341 {
342 this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length)); 342 this.Messaging.Write(ErrorMessages.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
343 } 343 }
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
index 6dcb1096..ef141795 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/GenerateTransformCommand.cs
@@ -5,7 +5,7 @@ namespace WixToolset.Core.WindowsInstaller
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Globalization; 7 using System.Globalization;
8 using WixToolset.Core.WindowsInstaller.Msi; 8 using WixToolset.Core.Native.Msi;
9 using WixToolset.Data; 9 using WixToolset.Data;
10 using WixToolset.Data.Symbols; 10 using WixToolset.Data.Symbols;
11 using WixToolset.Data.WindowsInstaller; 11 using WixToolset.Data.WindowsInstaller;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
index f8a1efd6..6446692e 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/MergeModulesCommand.cs
@@ -9,8 +9,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
9 using System.Linq; 9 using System.Linq;
10 using System.Runtime.InteropServices; 10 using System.Runtime.InteropServices;
11 using System.Text; 11 using System.Text;
12 using WixToolset.Core.Native; 12 using WixToolset.Core.Native.Msi;
13 using WixToolset.Core.WindowsInstaller.Msi; 13 using WixToolset.Core.Native.Msm;
14 using WixToolset.Data; 14 using WixToolset.Data;
15 using WixToolset.Data.Symbols; 15 using WixToolset.Data.Symbols;
16 using WixToolset.Data.WindowsInstaller; 16 using WixToolset.Data.WindowsInstaller;
@@ -60,8 +60,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
60 60
61 try 61 try
62 { 62 {
63 var interop = new MsmInterop(); 63 merge = MsmInterop.GetMsmMerge();
64 merge = interop.GetMsmMerge();
65 64
66 merge.OpenLog(logPath); 65 merge.OpenLog(logPath);
67 logOpen = true; 66 logOpen = true;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
index 8c66a9e1..039ba495 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessUncompressedFilesCommand.cs
@@ -6,7 +6,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.WindowsInstaller.Msi; 9 using WixToolset.Core.Native.Msi;
10 using WixToolset.Data; 10 using WixToolset.Data;
11 using WixToolset.Data.Symbols; 11 using WixToolset.Data.Symbols;
12 using WixToolset.Extensibility.Data; 12 using WixToolset.Extensibility.Data;
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index 9cd14cfa..0f77abfc 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -8,7 +8,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
8 using System.Globalization; 8 using System.Globalization;
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using WixToolset.Core.WindowsInstaller.Msi; 11 using WixToolset.Core.Native.Msi;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Symbols; 13 using WixToolset.Data.Symbols;
14 using WixToolset.Extensibility.Data; 14 using WixToolset.Extensibility.Data;
diff --git a/src/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs b/src/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
index 849cb361..aeda4443 100644
--- a/src/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
@@ -7,7 +7,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7 using System.ComponentModel; 7 using System.ComponentModel;
8 using System.IO; 8 using System.IO;
9 using System.Linq; 9 using System.Linq;
10 using WixToolset.Core.WindowsInstaller.Msi; 10 using WixToolset.Core.Native.Msi;
11 using WixToolset.Core.WindowsInstaller.Unbind; 11 using WixToolset.Core.WindowsInstaller.Unbind;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
diff --git a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
index 19869cfa..57f2f753 100644
--- a/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
@@ -8,8 +8,8 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
8 using System.IO; 8 using System.IO;
9 using System.Runtime.InteropServices; 9 using System.Runtime.InteropServices;
10 using System.Security.Cryptography.X509Certificates; 10 using System.Security.Cryptography.X509Certificates;
11 using WixToolset.Core.Native.Msi;
11 using WixToolset.Core.WindowsInstaller.Bind; 12 using WixToolset.Core.WindowsInstaller.Bind;
12 using WixToolset.Core.WindowsInstaller.Msi;
13 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Data.WindowsInstaller; 14 using WixToolset.Data.WindowsInstaller;
15 using WixToolset.Extensibility.Data; 15 using WixToolset.Extensibility.Data;
@@ -150,7 +150,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
150 Row digitalSignatureRow = null; 150 Row digitalSignatureRow = null;
151 151
152 var cabName = mediaRecord.GetString(4); // get the name of the cab 152 var cabName = mediaRecord.GetString(4); // get the name of the cab
153 // If there is no cabinet or it's an internal cab, skip it. 153 // If there is no cabinet or it's an internal cab, skip it.
154 if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal)) 154 if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
155 { 155 {
156 continue; 156 continue;
diff --git a/src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs b/src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs
deleted file mode 100644
index daf259b4..00000000
--- a/src/WixToolset.Core.WindowsInstaller/MergeMod/NativeMethods.cs
+++ /dev/null
@@ -1,508 +0,0 @@
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#if false
3namespace WixToolset.MergeMod
4{
5 using System;
6 using System.Collections;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
9
10 /// <summary>
11 /// Errors returned by merge operations.
12 /// </summary>
13 [Guid("0ADDA825-2C26-11D2-AD65-00A0C9AF11A6")]
14 internal enum MsmErrorType
15 {
16 /// <summary>
17 /// A request was made to open a module with a language not supported by the module.
18 /// No more general language is supported by the module.
19 /// Adds msmErrorLanguageUnsupported to the Type property and the requested language
20 /// to the Language Property (Error Object). All Error object properties are empty.
21 /// The OpenModule function returns ERROR_INSTALL_LANGUAGE_UNSUPPORTED (as HRESULT).
22 /// </summary>
23 msmErrorLanguageUnsupported = 1,
24
25 /// <summary>
26 /// A request was made to open a module with a supported language but the module has
27 /// an invalid language transform. Adds msmErrorLanguageFailed to the Type property
28 /// and the applied transform's language to the Language Property of the Error object.
29 /// This may not be the requested language if a more general language was used.
30 /// All other properties of the Error object are empty. The OpenModule function
31 /// returns ERROR_INSTALL_LANGUAGE_UNSUPPORTED (as HRESULT).
32 /// </summary>
33 msmErrorLanguageFailed = 2,
34
35 /// <summary>
36 /// The module cannot be merged because it excludes, or is excluded by, another module
37 /// in the database. Adds msmErrorExclusion to the Type property of the Error object.
38 /// The ModuleKeys property or DatabaseKeys property contains the primary keys of the
39 /// excluded module's row in the ModuleExclusion table. If an existing module excludes
40 /// the module being merged, the excluded module's ModuleSignature information is added
41 /// to ModuleKeys. If the module being merged excludes an existing module, DatabaseKeys
42 /// contains the excluded module's ModuleSignature information. All other properties
43 /// are empty (or -1).
44 /// </summary>
45 msmErrorExclusion = 3,
46
47 /// <summary>
48 /// Merge conflict during merge. The value of the Type property is set to
49 /// msmErrorTableMerge. The DatabaseTable property and DatabaseKeys property contain
50 /// the table name and primary keys of the conflicting row in the database. The
51 /// ModuleTable property and ModuleKeys property contain the table name and primary keys
52 /// of the conflicting row in the module. The ModuleTable and ModuleKeys entries may be
53 /// null if the row does not exist in the database. For example, if the conflict is in a
54 /// generated FeatureComponents table entry. On Windows Installer version 2.0, when
55 /// merging a configurable merge module, configuration may cause these properties to
56 /// refer to rows that do not exist in the module.
57 /// </summary>
58 msmErrorTableMerge = 4,
59
60 /// <summary>
61 /// There was a problem resequencing a sequence table to contain the necessary merged
62 /// actions. The Type property is set to msmErrorResequenceMerge. The DatabaseTable
63 /// and DatabaseKeys properties contain the sequence table name and primary keys
64 /// (action name) of the conflicting row. The ModuleTable and ModuleKeys properties
65 /// contain the sequence table name and primary key (action name) of the conflicting row.
66 /// On Windows Installer version 2.0, when merging a configurable merge module,
67 /// configuration may cause these properties to refer to rows that do not exist in the module.
68 /// </summary>
69 msmErrorResequenceMerge = 5,
70
71 /// <summary>
72 /// Not used.
73 /// </summary>
74 msmErrorFileCreate = 6,
75
76 /// <summary>
77 /// There was a problem creating a directory to extract a file to disk. The Path property
78 /// contains the directory that could not be created. All other properties are empty or -1.
79 /// Not available with Windows Installer version 1.0.
80 /// </summary>
81 msmErrorDirCreate = 7,
82
83 /// <summary>
84 /// A feature name is required to complete the merge, but no feature name was provided.
85 /// The Type property is set to msmErrorFeatureRequired. The DatabaseTable and DatabaseKeys
86 /// contain the table name and primary keys of the conflicting row. The ModuleTable and
87 /// ModuleKeys properties contain the table name and primary keys of the row cannot be merged.
88 /// On Windows Installer version 2.0, when merging a configurable merge module, configuration
89 /// may cause these properties to refer to rows that do not exist in the module.
90 /// If the failure is in a generated FeatureComponents table, the DatabaseTable and
91 /// DatabaseKeys properties are empty and the ModuleTable and ModuleKeys properties refer to
92 /// the row in the Component table causing the failure.
93 /// </summary>
94 msmErrorFeatureRequired = 8,
95
96 /// <summary>
97 /// Available with Window Installer version 2.0. Substitution of a Null value into a
98 /// non-nullable column. This enters msmErrorBadNullSubstitution in the Type property and
99 /// enters "ModuleSubstitution" and the keys from the ModuleSubstitution table for this row
100 /// into the ModuleTable property and ModuleKeys property. All other properties of the Error
101 /// object are set to an empty string or -1. This error causes the immediate failure of the
102 /// merge and the MergeEx function to return E_FAIL.
103 /// </summary>
104 msmErrorBadNullSubstitution = 9,
105
106 /// <summary>
107 /// Available with Window Installer version 2.0. Substitution of Text Format Type or Integer
108 /// Format Type into a Binary Type data column. This type of error returns
109 /// msmErrorBadSubstitutionType in the Type property and enters "ModuleSubstitution" and the
110 /// keys from the ModuleSubstitution table for this row into the ModuleTable property.
111 /// All other properties of the Error object are set to an empty string or -1. This error
112 /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
113 /// </summary>
114 msmErrorBadSubstitutionType = 10,
115
116 /// <summary>
117 /// Available with Window Installer Version 2.0. A row in the ModuleSubstitution table
118 /// references a configuration item not defined in the ModuleConfiguration table.
119 /// This type of error returns msmErrorMissingConfigItem in the Type property and enters
120 /// "ModuleSubstitution" and the keys from the ModuleSubstitution table for this row into
121 /// the ModuleTable property. All other properties of the Error object are set to an empty
122 /// string or -1. This error causes the immediate failure of the merge and the MergeEx
123 /// function to return E_FAIL.
124 /// </summary>
125 msmErrorMissingConfigItem = 11,
126
127 /// <summary>
128 /// Available with Window Installer version 2.0. The authoring tool has returned a Null
129 /// value for an item marked with the msmConfigItemNonNullable attribute. An error of this
130 /// type returns msmErrorBadNullResponse in the Type property and enters "ModuleSubstitution"
131 /// and the keys from the ModuleSubstitution table for for the item into the ModuleTable property.
132 /// All other properties of the Error object are set to an empty string or -1. This error
133 /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
134 /// </summary>
135 msmErrorBadNullResponse = 12,
136
137 /// <summary>
138 /// Available with Window Installer version 2.0. The authoring tool returned a failure code
139 /// (not S_OK or S_FALSE) when asked for data. An error of this type will return
140 /// msmErrorDataRequestFailed in the Type property and enters "ModuleSubstitution"
141 /// and the keys from the ModuleSubstitution table for the item into the ModuleTable property.
142 /// All other properties of the Error object are set to an empty string or -1. This error
143 /// causes the immediate failure of the merge and the MergeEx function to return E_FAIL.
144 /// </summary>
145 msmErrorDataRequestFailed = 13,
146
147 /// <summary>
148 /// Available with Windows Installer 2.0 and later versions. Indicates that an attempt was
149 /// made to merge a 64-bit module into a package that was not a 64-bit package. An error of
150 /// this type returns msmErrorPlatformMismatch in the Type property. All other properties of
151 /// the error object are set to an empty string or -1. This error causes the immediate failure
152 /// of the merge and causes the Merge function or MergeEx function to return E_FAIL.
153 /// </summary>
154 msmErrorPlatformMismatch = 14,
155 }
156
157 /// <summary>
158 /// IMsmMerge2 interface.
159 /// </summary>
160 [ComImport, Guid("351A72AB-21CB-47ab-B7AA-C4D7B02EA305")]
161 internal interface IMsmMerge2
162 {
163 /// <summary>
164 /// The OpenDatabase method of the Merge object opens a Windows Installer installation
165 /// database, located at a specified path, that is to be merged with a module.
166 /// </summary>
167 /// <param name="path">Path to the database being opened.</param>
168 void OpenDatabase(string path);
169
170 /// <summary>
171 /// The OpenModule method of the Merge object opens a Windows Installer merge module
172 /// in read-only mode. A module must be opened before it can be merged with an installation database.
173 /// </summary>
174 /// <param name="fileName">Fully qualified file name pointing to a merge module.</param>
175 /// <param name="language">A valid language identifier (LANGID).</param>
176 void OpenModule(string fileName, short language);
177
178 /// <summary>
179 /// The CloseDatabase method of the Merge object closes the currently open Windows Installer database.
180 /// </summary>
181 /// <param name="commit">true if changes should be saved, false otherwise.</param>
182 void CloseDatabase(bool commit);
183
184 /// <summary>
185 /// The CloseModule method of the Merge object closes the currently open Windows Installer merge module.
186 /// </summary>
187 void CloseModule();
188
189 /// <summary>
190 /// The OpenLog method of the Merge object opens a log file that receives progress and error messages.
191 /// If the log file already exists, the installer appends new messages. If the log file does not exist,
192 /// the installer creates a log file.
193 /// </summary>
194 /// <param name="fileName">Fully qualified filename pointing to a file to open or create.</param>
195 void OpenLog(string fileName);
196
197 /// <summary>
198 /// The CloseLog method of the Merge object closes the current log file.
199 /// </summary>
200 void CloseLog();
201
202 /// <summary>
203 /// The Log method of the Merge object writes a text string to the currently open log file.
204 /// </summary>
205 /// <param name="message">The text string to display.</param>
206 void Log(string message);
207
208 /// <summary>
209 /// Gets the errors from the last merge operation.
210 /// </summary>
211 /// <value>The errors from the last merge operation.</value>
212 IMsmErrors Errors
213 {
214 get;
215 }
216
217 /// <summary>
218 /// Gets a collection of Dependency objects that enumerates a set of unsatisfied dependencies for the current database.
219 /// </summary>
220 /// <value>A collection of Dependency objects that enumerates a set of unsatisfied dependencies for the current database.</value>
221 object Dependencies
222 {
223 get;
224 }
225
226 /// <summary>
227 /// The Merge method of the Merge object executes a merge of the current database and current
228 /// module. The merge attaches the components in the module to the feature identified by Feature.
229 /// The root of the module's directory tree is redirected to the location given by RedirectDir.
230 /// </summary>
231 /// <param name="feature">The name of a feature in the database.</param>
232 /// <param name="redirectDir">The key of an entry in the Directory table of the database.
233 /// This parameter may be NULL or an empty string.</param>
234 void Merge(string feature, string redirectDir);
235
236 /// <summary>
237 /// The Connect method of the Merge object connects a module to an additional feature.
238 /// The module must have already been merged into the database or will be merged into the database.
239 /// The feature must exist before calling this function.
240 /// </summary>
241 /// <param name="feature">The name of a feature already existing in the database.</param>
242 void Connect(string feature);
243
244 /// <summary>
245 /// The ExtractCAB method of the Merge object extracts the embedded .cab file from a module and
246 /// saves it as the specified file. The installer creates this file if it does not already exist
247 /// and overwritten if it does exist.
248 /// </summary>
249 /// <param name="fileName">The fully qualified destination file.</param>
250 void ExtractCAB(string fileName);
251
252 /// <summary>
253 /// The ExtractFiles method of the Merge object extracts the embedded .cab file from a module
254 /// and then writes those files to the destination directory.
255 /// </summary>
256 /// <param name="path">The fully qualified destination directory.</param>
257 void ExtractFiles(string path);
258
259 /// <summary>
260 /// The MergeEx method of the Merge object is equivalent to the Merge function, except that it
261 /// takes an extra argument. The Merge method executes a merge of the current database and
262 /// current module. The merge attaches the components in the module to the feature identified
263 /// by Feature. The root of the module's directory tree is redirected to the location given by RedirectDir.
264 /// </summary>
265 /// <param name="feature">The name of a feature in the database.</param>
266 /// <param name="redirectDir">The key of an entry in the Directory table of the database. This parameter may
267 /// be NULL or an empty string.</param>
268 /// <param name="configuration">The pConfiguration argument is an interface implemented by the client. The argument may
269 /// be NULL. The presence of this argument indicates that the client is capable of supporting the configuration
270 /// functionality, but does not obligate the client to provide configuration data for any specific configurable item.</param>
271 void MergeEx(string feature, string redirectDir, IMsmConfigureModule configuration);
272
273 /// <summary>
274 /// The ExtractFilesEx method of the Merge object extracts the embedded .cab file from a module and
275 /// then writes those files to the destination directory.
276 /// </summary>
277 /// <param name="path">The fully qualified destination directory.</param>
278 /// <param name="longFileNames">Set to specify using long file names for path segments and final file names.</param>
279 /// <param name="filePaths">This is a list of fully-qualified paths for the files that were successfully extracted.
280 /// The list is empty if no files can be extracted. This argument may be null. No list is provided if pFilePaths is null.</param>
281 void ExtractFilesEx(string path, bool longFileNames, ref IntPtr filePaths);
282
283 /// <summary>
284 /// Gets a collection ConfigurableItem objects, each of which represents a single row from the ModuleConfiguration table.
285 /// </summary>
286 /// <value>A collection ConfigurableItem objects, each of which represents a single row from the ModuleConfiguration table.</value>
287 /// <remarks>Semantically, each interface in the enumerator represents an item that can be configured by the module consumer.
288 /// The collection is a read-only collection and implements the standard read-only collection interfaces of Item(), Count() and _NewEnum().
289 /// The IEnumMsmConfigItems enumerator implements Next(), Skip(), Reset(), and Clone() with the standard semantics.</remarks>
290 object ConfigurableItems
291 {
292 get;
293 }
294
295 /// <summary>
296 /// The CreateSourceImage method of the Merge object allows the client to extract the files from a module to
297 /// a source image on disk after a merge, taking into account changes to the module that might have been made
298 /// during module configuration. The list of files to be extracted is taken from the file table of the module
299 /// during the merge process. The list of files consists of every file successfully copied from the file table
300 /// of the module to the target database. File table entries that were not copied due to primary key conflicts
301 /// with existing rows in the database are not a part of this list. At image creation time, the directory for
302 /// each of these files comes from the open (post-merge) database. The path specified in the Path parameter is
303 /// the root of the source image for the install. fLongFileNames determines whether or not long file names are
304 /// used for both path segments and final file names. The function fails if no database is open, no module is
305 /// open, or no merge has been performed.
306 /// </summary>
307 /// <param name="path">The path of the root of the source image for the install.</param>
308 /// <param name="longFileNames">Determines whether or not long file names are used for both path segments and final file names. </param>
309 /// <param name="filePaths">This is a list of fully-qualified paths for the files that were successfully extracted.
310 /// The list is empty if no files can be extracted. This argument may be null. No list is provided if pFilePaths is null.</param>
311 void CreateSourceImage(string path, bool longFileNames, ref IntPtr filePaths);
312
313 /// <summary>
314 /// The get_ModuleFiles function implements the ModuleFiles property of the GetFiles object. This function
315 /// returns the primary keys in the File table of the currently open module. The primary keys are returned
316 /// as a collection of strings. The module must be opened by a call to the OpenModule function before calling get_ModuleFiles.
317 /// </summary>
318 IMsmStrings ModuleFiles
319 {
320 get;
321 }
322 }
323
324 /// <summary>
325 /// Collection of merge errors.
326 /// </summary>
327 [ComImport, Guid("0ADDA82A-2C26-11D2-AD65-00A0C9AF11A6")]
328 internal interface IMsmErrors
329 {
330 /// <summary>
331 /// Gets the IMsmError at the specified index.
332 /// </summary>
333 /// <param name="index">The one-based index of the IMsmError to get.</param>
334 IMsmError this[int index]
335 {
336 get;
337 }
338
339 /// <summary>
340 /// Gets the count of IMsmErrors in this collection.
341 /// </summary>
342 /// <value>The count of IMsmErrors in this collection.</value>
343 int Count
344 {
345 get;
346 }
347 }
348
349 /// <summary>
350 /// A merge error.
351 /// </summary>
352 [ComImport, Guid("0ADDA828-2C26-11D2-AD65-00A0C9AF11A6")]
353 internal interface IMsmError
354 {
355 /// <summary>
356 /// Gets the type of merge error.
357 /// </summary>
358 /// <value>The type of merge error.</value>
359 MsmErrorType Type
360 {
361 get;
362 }
363
364 /// <summary>
365 /// Gets the path information from the merge error.
366 /// </summary>
367 /// <value>The path information from the merge error.</value>
368 string Path
369 {
370 get;
371 }
372
373 /// <summary>
374 /// Gets the language information from the merge error.
375 /// </summary>
376 /// <value>The language information from the merge error.</value>
377 short Language
378 {
379 get;
380 }
381
382 /// <summary>
383 /// Gets the database table from the merge error.
384 /// </summary>
385 /// <value>The database table from the merge error.</value>
386 string DatabaseTable
387 {
388 get;
389 }
390
391 /// <summary>
392 /// Gets the collection of database keys from the merge error.
393 /// </summary>
394 /// <value>The collection of database keys from the merge error.</value>
395 IMsmStrings DatabaseKeys
396 {
397 get;
398 }
399
400 /// <summary>
401 /// Gets the module table from the merge error.
402 /// </summary>
403 /// <value>The module table from the merge error.</value>
404 string ModuleTable
405 {
406 get;
407 }
408
409 /// <summary>
410 /// Gets the collection of module keys from the merge error.
411 /// </summary>
412 /// <value>The collection of module keys from the merge error.</value>
413 IMsmStrings ModuleKeys
414 {
415 get;
416 }
417 }
418
419 /// <summary>
420 /// A collection of strings.
421 /// </summary>
422 [ComImport, Guid("0ADDA827-2C26-11D2-AD65-00A0C9AF11A6")]
423 internal interface IMsmStrings
424 {
425 /// <summary>
426 /// Gets the string at the specified index.
427 /// </summary>
428 /// <param name="index">The one-based index of the string to get.</param>
429 string this[int index]
430 {
431 get;
432 }
433
434 /// <summary>
435 /// Gets the count of strings in this collection.
436 /// </summary>
437 /// <value>The count of strings in this collection.</value>
438 int Count
439 {
440 get;
441 }
442 }
443
444 /// <summary>
445 /// Callback for configurable merge modules.
446 /// </summary>
447 [ComImport, Guid("AC013209-18A7-4851-8A21-2353443D70A0"), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
448 internal interface IMsmConfigureModule
449 {
450 /// <summary>
451 /// Callback to retrieve text data for configurable merge modules.
452 /// </summary>
453 /// <param name="name">Name of the data to be retrieved.</param>
454 /// <param name="configData">The data corresponding to the name.</param>
455 /// <returns>The error code (HRESULT).</returns>
456 [PreserveSig]
457 int ProvideTextData([In, MarshalAs(UnmanagedType.BStr)] string name, [MarshalAs(UnmanagedType.BStr)] out string configData);
458
459 /// <summary>
460 /// Callback to retrieve integer data for configurable merge modules.
461 /// </summary>
462 /// <param name="name">Name of the data to be retrieved.</param>
463 /// <param name="configData">The data corresponding to the name.</param>
464 /// <returns>The error code (HRESULT).</returns>
465 [PreserveSig]
466 int ProvideIntegerData([In, MarshalAs(UnmanagedType.BStr)] string name, out int configData);
467 }
468
469 /// <summary>
470 /// Merge merge modules into an MSI file.
471 /// </summary>
472 [ComImport, Guid("F94985D5-29F9-4743-9805-99BC3F35B678")]
473 internal class MsmMerge2
474 {
475 }
476
477 /// <summary>
478 /// Defines the standard COM IClassFactory interface.
479 /// </summary>
480 [ComImport, Guid("00000001-0000-0000-C000-000000000046")]
481 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
482 internal interface IClassFactory
483 {
484 [return:MarshalAs(UnmanagedType.IUnknown)]
485 object CreateInstance(IntPtr unkOuter, [MarshalAs(UnmanagedType.LPStruct)] Guid iid);
486 }
487
488 /// <summary>
489 /// Contains native methods for merge operations.
490 /// </summary>
491 internal class NativeMethods
492 {
493 [DllImport("mergemod.dll", EntryPoint="DllGetClassObject", PreserveSig=false)]
494 [return: MarshalAs(UnmanagedType.IUnknown)]
495 private static extern object MergeModGetClassObject([MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [MarshalAs(UnmanagedType.LPStruct)] Guid iid);
496
497 /// <summary>
498 /// Load the merge object directly from a local mergemod.dll without going through COM registration.
499 /// </summary>
500 /// <returns>Merge interface.</returns>
501 internal static IMsmMerge2 GetMsmMerge()
502 {
503 IClassFactory classFactory = (IClassFactory) MergeModGetClassObject(typeof(MsmMerge2).GUID, typeof(IClassFactory).GUID);
504 return (IMsmMerge2) classFactory.CreateInstance(IntPtr.Zero, typeof(IMsmMerge2).GUID);
505 }
506 }
507}
508#endif \ No newline at end of file
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
deleted file mode 100644
index 9f08a217..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/Database.cs
+++ /dev/null
@@ -1,248 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Globalization;
7 using System.IO;
8 using System.Threading;
9
10 /// <summary>
11 /// Wrapper class for managing MSI API database handles.
12 /// </summary>
13 internal sealed class Database : MsiHandle
14 {
15 private const int STG_E_LOCKVIOLATION = unchecked((int)0x80030021);
16
17 /// <summary>
18 /// Constructor that opens an MSI database.
19 /// </summary>
20 /// <param name="path">Path to the database to be opened.</param>
21 /// <param name="type">Persist mode to use when opening the database.</param>
22 public Database(string path, OpenDatabase type)
23 {
24 int error = MsiInterop.MsiOpenDatabase(path, new IntPtr((int)type), out var handle);
25 if (0 != error)
26 {
27 throw new MsiException(error);
28 }
29 this.Handle = handle;
30 }
31
32 public void ApplyTransform(string transformFile)
33 {
34 // get the curret validation bits
35 TransformErrorConditions conditions = TransformErrorConditions.None;
36 using (SummaryInformation summaryInfo = new SummaryInformation(transformFile))
37 {
38 string value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags);
39 try
40 {
41 int validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture);
42 conditions = (TransformErrorConditions)(validationFlags & 0xffff);
43 }
44 catch (FormatException)
45 {
46 // fallback to default of None
47 }
48 }
49
50 this.ApplyTransform(transformFile, conditions);
51 }
52
53 /// <summary>
54 /// Applies a transform to this database.
55 /// </summary>
56 /// <param name="transformFile">Path to the transform file being applied.</param>
57 /// <param name="errorConditions">Specifies the error conditions that are to be suppressed.</param>
58 public void ApplyTransform(string transformFile, TransformErrorConditions errorConditions)
59 {
60 int error = MsiInterop.MsiDatabaseApplyTransform(this.Handle, transformFile, errorConditions);
61 if (0 != error)
62 {
63 throw new MsiException(error);
64 }
65 }
66
67 /// <summary>
68 /// Commits changes made to the database.
69 /// </summary>
70 public void Commit()
71 {
72 // Retry this call 3 times to deal with an MSI internal locking problem.
73 const int retryWait = 300;
74 const int retryLimit = 3;
75 int error = 0;
76
77 for (int i = 1; i <= retryLimit; ++i)
78 {
79 error = MsiInterop.MsiDatabaseCommit(this.Handle);
80
81 if (0 == error)
82 {
83 return;
84 }
85 else
86 {
87 MsiException exception = new MsiException(error);
88
89 // We need to see if the error code is contained in any of the strings in ErrorInfo.
90 // Join the array together and search for the error code to cover the string array.
91 if (!String.Join(", ", exception.ErrorInfo).Contains(STG_E_LOCKVIOLATION.ToString()))
92 {
93 break;
94 }
95
96 Console.Error.WriteLine(String.Format("Failed to create the database. Info: {0}. Retrying ({1} of {2})", String.Join(", ", exception.ErrorInfo), i, retryLimit));
97 Thread.Sleep(retryWait);
98 }
99 }
100
101 throw new MsiException(error);
102 }
103
104 /// <summary>
105 /// Creates and populates the summary information stream of an existing transform file.
106 /// </summary>
107 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
108 /// <param name="transformFile">The name of the generated transform file.</param>
109 /// <param name="errorConditions">Required error conditions that should be suppressed when the transform is applied.</param>
110 /// <param name="validations">Required when the transform is applied to a database;
111 /// shows which properties should be validated to verify that this transform can be applied to the database.</param>
112 public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations)
113 {
114 int error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, transformFile, errorConditions, validations);
115 if (0 != error)
116 {
117 throw new MsiException(error);
118 }
119 }
120
121 /// <summary>
122 /// Imports an installer text archive table (idt file) into an open database.
123 /// </summary>
124 /// <param name="idtPath">Specifies the path to the file to import.</param>
125 /// <exception cref="WixInvalidIdtException">Attempted to import an IDT file with an invalid format or unsupported data.</exception>
126 /// <exception cref="MsiException">Another error occured while importing the IDT file.</exception>
127 public void Import(string idtPath)
128 {
129 string folderPath = Path.GetFullPath(Path.GetDirectoryName(idtPath));
130 string fileName = Path.GetFileName(idtPath);
131
132 int error = MsiInterop.MsiDatabaseImport(this.Handle, folderPath, fileName);
133 if (1627 == error) // ERROR_FUNCTION_FAILED
134 {
135 throw new WixInvalidIdtException(idtPath);
136 }
137 else if (0 != error)
138 {
139 throw new MsiException(error);
140 }
141 }
142
143 /// <summary>
144 /// Exports an installer table from an open database to a text archive file (idt file).
145 /// </summary>
146 /// <param name="tableName">Specifies the name of the table to export.</param>
147 /// <param name="folderPath">Specifies the name of the folder that contains archive files. If null or empty string, uses current directory.</param>
148 /// <param name="fileName">Specifies the name of the exported table archive file.</param>
149 public void Export(string tableName, string folderPath, string fileName)
150 {
151 if (String.IsNullOrEmpty(folderPath))
152 {
153 folderPath = Environment.CurrentDirectory;
154 }
155
156 int error = MsiInterop.MsiDatabaseExport(this.Handle, tableName, folderPath, fileName);
157 if (0 != error)
158 {
159 throw new MsiException(error);
160 }
161 }
162
163 /// <summary>
164 /// Creates a transform that, when applied to the reference database, results in this database.
165 /// </summary>
166 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
167 /// <param name="transformFile">The name of the generated transform file. This is optional.</param>
168 /// <returns>true if a transform is generated; false if a transform is not generated because
169 /// there are no differences between the two databases.</returns>
170 public bool GenerateTransform(Database referenceDatabase, string transformFile)
171 {
172 int error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, transformFile, 0, 0);
173 if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found
174 {
175 throw new MsiException(error);
176 }
177
178 return (0xE8 != error);
179 }
180
181 /// <summary>
182 /// Merges two databases together.
183 /// </summary>
184 /// <param name="mergeDatabase">The database to merge into the base database.</param>
185 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
186 public void Merge(Database mergeDatabase, string tableName)
187 {
188 int error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName);
189 if (0 != error)
190 {
191 throw new MsiException(error);
192 }
193 }
194
195 /// <summary>
196 /// Prepares a database query and creates a <see cref="View">View</see> object.
197 /// </summary>
198 /// <param name="query">Specifies a SQL query string for querying the database.</param>
199 /// <returns>A view object is returned if the query was successful.</returns>
200 public View OpenView(string query)
201 {
202 return new View(this, query);
203 }
204
205 /// <summary>
206 /// Prepares and executes a database query and creates a <see cref="View">View</see> object.
207 /// </summary>
208 /// <param name="query">Specifies a SQL query string for querying the database.</param>
209 /// <returns>A view object is returned if the query was successful.</returns>
210 public View OpenExecuteView(string query)
211 {
212 View view = new View(this, query);
213
214 view.Execute();
215 return view;
216 }
217
218 /// <summary>
219 /// Verifies the existence or absence of a table.
220 /// </summary>
221 /// <param name="tableName">Table name to to verify the existence of.</param>
222 /// <returns>Returns true if the table exists, false if it does not.</returns>
223 public bool TableExists(string tableName)
224 {
225 int result = MsiInterop.MsiDatabaseIsTablePersistent(this.Handle, tableName);
226 return MsiInterop.MSICONDITIONTRUE == result;
227 }
228
229 /// <summary>
230 /// Returns a <see cref="Record">Record</see> containing the names of all the primary
231 /// key columns for a specified table.
232 /// </summary>
233 /// <param name="tableName">Specifies the name of the table from which to obtain
234 /// primary key names.</param>
235 /// <returns>Returns a <see cref="Record">Record</see> containing the names of all the
236 /// primary key columns for a specified table.</returns>
237 public Record PrimaryKeys(string tableName)
238 {
239 var error = MsiInterop.MsiDatabaseGetPrimaryKeys(this.Handle, tableName, out var recordHandle);
240 if (error != 0)
241 {
242 throw new MsiException(error);
243 }
244
245 return new Record(recordHandle);
246 }
247 }
248}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
deleted file mode 100644
index 0edcd633..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/Installer.cs
+++ /dev/null
@@ -1,363 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Diagnostics;
7 using System.Text;
8 using WixToolset.Core.Native;
9
10 /// <summary>
11 /// Windows Installer message types.
12 /// </summary>
13 [Flags]
14 internal enum InstallMessage
15 {
16 /// <summary>
17 /// Premature termination, possibly fatal out of memory.
18 /// </summary>
19 FatalExit = 0x00000000,
20
21 /// <summary>
22 /// Formatted error message, [1] is message number in Error table.
23 /// </summary>
24 Error = 0x01000000,
25
26 /// <summary>
27 /// Formatted warning message, [1] is message number in Error table.
28 /// </summary>
29 Warning = 0x02000000,
30
31 /// <summary>
32 /// User request message, [1] is message number in Error table.
33 /// </summary>
34 User = 0x03000000,
35
36 /// <summary>
37 /// Informative message for log, not to be displayed.
38 /// </summary>
39 Info = 0x04000000,
40
41 /// <summary>
42 /// List of files in use that need to be replaced.
43 /// </summary>
44 FilesInUse = 0x05000000,
45
46 /// <summary>
47 /// Request to determine a valid source location.
48 /// </summary>
49 ResolveSource = 0x06000000,
50
51 /// <summary>
52 /// Insufficient disk space message.
53 /// </summary>
54 OutOfDiskSpace = 0x07000000,
55
56 /// <summary>
57 /// Progress: start of action, [1] action name, [2] description, [3] template for ACTIONDATA messages.
58 /// </summary>
59 ActionStart = 0x08000000,
60
61 /// <summary>
62 /// Action data. Record fields correspond to the template of ACTIONSTART message.
63 /// </summary>
64 ActionData = 0x09000000,
65
66 /// <summary>
67 /// Progress bar information. See the description of record fields below.
68 /// </summary>
69 Progress = 0x0A000000,
70
71 /// <summary>
72 /// To enable the Cancel button set [1] to 2 and [2] to 1. To disable the Cancel button set [1] to 2 and [2] to 0.
73 /// </summary>
74 CommonData = 0x0B000000,
75
76 /// <summary>
77 /// Sent prior to UI initialization, no string data.
78 /// </summary>
79 Initilize = 0x0C000000,
80
81 /// <summary>
82 /// Sent after UI termination, no string data.
83 /// </summary>
84 Terminate = 0x0D000000,
85
86 /// <summary>
87 /// Sent prior to display or authored dialog or wizard.
88 /// </summary>
89 ShowDialog = 0x0E000000
90 }
91
92 /// <summary>
93 /// Windows Installer log modes.
94 /// </summary>
95 [Flags]
96 internal enum InstallLogModes
97 {
98 /// <summary>
99 /// Premature termination of installation.
100 /// </summary>
101 FatalExit = (1 << ((int)InstallMessage.FatalExit >> 24)),
102
103 /// <summary>
104 /// The error messages are logged.
105 /// </summary>
106 Error = (1 << ((int)InstallMessage.Error >> 24)),
107
108 /// <summary>
109 /// The warning messages are logged.
110 /// </summary>
111 Warning = (1 << ((int)InstallMessage.Warning >> 24)),
112
113 /// <summary>
114 /// The user requests are logged.
115 /// </summary>
116 User = (1 << ((int)InstallMessage.User >> 24)),
117
118 /// <summary>
119 /// The status messages that are not displayed are logged.
120 /// </summary>
121 Info = (1 << ((int)InstallMessage.Info >> 24)),
122
123 /// <summary>
124 /// Request to determine a valid source location.
125 /// </summary>
126 ResolveSource = (1 << ((int)InstallMessage.ResolveSource >> 24)),
127
128 /// <summary>
129 /// The was insufficient disk space.
130 /// </summary>
131 OutOfDiskSpace = (1 << ((int)InstallMessage.OutOfDiskSpace >> 24)),
132
133 /// <summary>
134 /// The start of new installation actions are logged.
135 /// </summary>
136 ActionStart = (1 << ((int)InstallMessage.ActionStart >> 24)),
137
138 /// <summary>
139 /// The data record with the installation action is logged.
140 /// </summary>
141 ActionData = (1 << ((int)InstallMessage.ActionData >> 24)),
142
143 /// <summary>
144 /// The parameters for user-interface initialization are logged.
145 /// </summary>
146 CommonData = (1 << ((int)InstallMessage.CommonData >> 24)),
147
148 /// <summary>
149 /// Logs the property values at termination.
150 /// </summary>
151 PropertyDump = (1 << ((int)InstallMessage.Progress >> 24)),
152
153 /// <summary>
154 /// Sends large amounts of information to a log file not generally useful to users.
155 /// May be used for technical support.
156 /// </summary>
157 Verbose = (1 << ((int)InstallMessage.Initilize >> 24)),
158
159 /// <summary>
160 /// Sends extra debugging information, such as handle creation information, to the log file.
161 /// </summary>
162 ExtraDebug = (1 << ((int)InstallMessage.Terminate >> 24)),
163
164 /// <summary>
165 /// Progress bar information. This message includes information on units so far and total number of units.
166 /// See MsiProcessMessage for an explanation of the message format.
167 /// This message is only sent to an external user interface and is not logged.
168 /// </summary>
169 Progress = (1 << ((int)InstallMessage.Progress >> 24)),
170
171 /// <summary>
172 /// If this is not a quiet installation, then the basic UI has been initialized.
173 /// If this is a full UI installation, the full UI is not yet initialized.
174 /// This message is only sent to an external user interface and is not logged.
175 /// </summary>
176 Initialize = (1 << ((int)InstallMessage.Initilize >> 24)),
177
178 /// <summary>
179 /// If a full UI is being used, the full UI has ended.
180 /// If this is not a quiet installation, the basic UI has not yet ended.
181 /// This message is only sent to an external user interface and is not logged.
182 /// </summary>
183 Terminate = (1 << ((int)InstallMessage.Terminate >> 24)),
184
185 /// <summary>
186 /// Sent prior to display of the full UI dialog.
187 /// This message is only sent to an external user interface and is not logged.
188 /// </summary>
189 ShowDialog = (1 << ((int)InstallMessage.ShowDialog >> 24)),
190
191 /// <summary>
192 /// Files in use information. When this message is received, a FilesInUse Dialog should be displayed.
193 /// </summary>
194 FilesInUse = (1 << ((int)InstallMessage.FilesInUse >> 24))
195 }
196
197 /// <summary>
198 /// Windows Installer UI levels.
199 /// </summary>
200 [Flags]
201 internal enum InstallUILevels
202 {
203 /// <summary>
204 /// No change in the UI level. However, if phWnd is not Null, the parent window can change.
205 /// </summary>
206 NoChange = 0,
207
208 /// <summary>
209 /// The installer chooses an appropriate user interface level.
210 /// </summary>
211 Default = 1,
212
213 /// <summary>
214 /// Completely silent installation.
215 /// </summary>
216 None = 2,
217
218 /// <summary>
219 /// Simple progress and error handling.
220 /// </summary>
221 Basic = 3,
222
223 /// <summary>
224 /// Authored user interface with wizard dialog boxes suppressed.
225 /// </summary>
226 Reduced = 4,
227
228 /// <summary>
229 /// Authored user interface with wizards, progress, and errors.
230 /// </summary>
231 Full = 5,
232
233 /// <summary>
234 /// If combined with the Basic value, the installer shows simple progress dialog boxes but
235 /// does not display a Cancel button on the dialog. This prevents users from canceling the install.
236 /// Available with Windows Installer version 2.0.
237 /// </summary>
238 HideCancel = 0x20,
239
240 /// <summary>
241 /// If combined with the Basic value, the installer shows simple progress
242 /// dialog boxes but does not display any modal dialog boxes or error dialog boxes.
243 /// </summary>
244 ProgressOnly = 0x40,
245
246 /// <summary>
247 /// If combined with any above value, the installer displays a modal dialog
248 /// box at the end of a successful installation or if there has been an error.
249 /// No dialog box is displayed if the user cancels.
250 /// </summary>
251 EndDialog = 0x80,
252
253 /// <summary>
254 /// If this value is combined with the None value, the installer displays only the dialog
255 /// boxes used for source resolution. No other dialog boxes are shown. This value has no
256 /// effect if the UI level is not INSTALLUILEVEL_NONE. It is used with an external user
257 /// interface designed to handle all of the UI except for source resolution. In this case,
258 /// the installer handles source resolution. This value is only available with Windows Installer 2.0 and later.
259 /// </summary>
260 SourceResOnly = 0x100
261 }
262
263 /// <summary>
264 /// Represents the Windows Installer, provides wrappers to
265 /// create the top-level objects and access their methods.
266 /// </summary>
267 internal sealed class Installer
268 {
269 /// <summary>
270 /// Protect the constructor.
271 /// </summary>
272 private Installer()
273 {
274 }
275
276 /// <summary>
277 /// Takes the path to a file and returns a 128-bit hash of that file.
278 /// </summary>
279 /// <param name="filePath">Path to file that is to be hashed.</param>
280 /// <param name="options">The value in this column must be 0. This parameter is reserved for future use.</param>
281 /// <param name="hash">Int array that receives the returned file hash information.</param>
282 internal static void GetFileHash(string filePath, int options, out int[] hash)
283 {
284 MSIFILEHASHINFO hashInterop = new MSIFILEHASHINFO();
285 hashInterop.FileHashInfoSize = 20;
286
287 int error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop);
288 if (0 != error)
289 {
290 throw new MsiException(error);
291 }
292
293 Debug.Assert(20 == hashInterop.FileHashInfoSize);
294
295 hash = new int[4];
296 hash[0] = hashInterop.Data0;
297 hash[1] = hashInterop.Data1;
298 hash[2] = hashInterop.Data2;
299 hash[3] = hashInterop.Data3;
300 }
301
302 /// <summary>
303 /// Returns the version string and language string in the format that the installer
304 /// expects to find them in the database. If you just want version information, set
305 /// lpLangBuf and pcchLangBuf to zero. If you just want language information, set
306 /// lpVersionBuf and pcchVersionBuf to zero.
307 /// </summary>
308 /// <param name="filePath">Specifies the path to the file.</param>
309 /// <param name="version">Returns the file version. Set to 0 for language information only.</param>
310 /// <param name="language">Returns the file language. Set to 0 for version information only.</param>
311 internal static void GetFileVersion(string filePath, out string version, out string language)
312 {
313 int versionLength = 20;
314 int languageLength = 20;
315 StringBuilder versionBuffer = new StringBuilder(versionLength);
316 StringBuilder languageBuffer = new StringBuilder(languageLength);
317
318 int error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
319 if (234 == error)
320 {
321 versionBuffer.EnsureCapacity(++versionLength);
322 languageBuffer.EnsureCapacity(++languageLength);
323 error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength);
324 }
325 else if (1006 == error)
326 {
327 // file has no version or language, so no error
328 error = 0;
329 }
330
331 if (0 != error)
332 {
333 throw new MsiException(error);
334 }
335
336 version = versionBuffer.ToString();
337 language = languageBuffer.ToString();
338 }
339
340 /// <summary>
341 /// Enables an external user-interface handler.
342 /// </summary>
343 /// <param name="installUIHandler">Specifies a callback function.</param>
344 /// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param>
345 /// <param name="context">Pointer to an application context that is passed to the callback function.</param>
346 /// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns>
347 internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context)
348 {
349 return MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context);
350 }
351
352 /// <summary>
353 /// Enables the installer's internal user interface.
354 /// </summary>
355 /// <param name="uiLevel">Specifies the level of complexity of the user interface.</param>
356 /// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.</param>
357 /// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns>
358 internal static int SetInternalUI(int uiLevel, ref IntPtr hwnd)
359 {
360 return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd);
361 }
362 }
363}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
deleted file mode 100644
index 4e324a60..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/MsiException.cs
+++ /dev/null
@@ -1,77 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.ComponentModel;
7
8 /// <summary>
9 /// Exception that wraps MsiGetLastError().
10 /// </summary>
11 [Serializable]
12 public class MsiException : Win32Exception
13 {
14 /// <summary>
15 /// Instantiate a new MsiException with a given error.
16 /// </summary>
17 /// <param name="error">The error code from the MsiXxx() function call.</param>
18 public MsiException(int error) : base(error)
19 {
20 uint handle = MsiInterop.MsiGetLastErrorRecord();
21 if (0 != handle)
22 {
23 using (Record record = new Record(handle))
24 {
25 this.MsiError = record.GetInteger(1);
26
27 int errorInfoCount = record.GetFieldCount() - 1;
28 this.ErrorInfo = new string[errorInfoCount];
29 for (int i = 0; i < errorInfoCount; ++i)
30 {
31 this.ErrorInfo[i] = record.GetString(i + 2);
32 }
33 }
34 }
35 else
36 {
37 this.MsiError = 0;
38 this.ErrorInfo = new string[0];
39 }
40
41 this.Error = error;
42 }
43
44 /// <summary>
45 /// Gets the error number.
46 /// </summary>
47 public int Error { get; private set; }
48
49 /// <summary>
50 /// Gets the internal MSI error number.
51 /// </summary>
52 public int MsiError { get; private set; }
53
54 /// <summary>
55 /// Gets any additional the error information.
56 /// </summary>
57 public string[] ErrorInfo { get; private set; }
58
59 /// <summary>
60 /// Overrides Message property to return useful error message.
61 /// </summary>
62 public override string Message
63 {
64 get
65 {
66 if (0 == this.MsiError)
67 {
68 return base.Message;
69 }
70 else
71 {
72 return String.Format("Internal MSI failure. Win32 error: {0}, MSI error: {1}, detail: {2}", this.Error, this.MsiError, String.Join(", ", this.ErrorInfo));
73 }
74 }
75 }
76 }
77}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
deleted file mode 100644
index 9006180b..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/MsiHandle.cs
+++ /dev/null
@@ -1,117 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.ComponentModel;
7#if !DEBUG
8 using System.Diagnostics;
9#endif
10 using System.Threading;
11
12 /// <summary>
13 /// Wrapper class for MSI handle.
14 /// </summary>
15 public class MsiHandle : IDisposable
16 {
17 private bool disposed;
18 private uint handle;
19 private int owningThread;
20#if DEBUG
21 private string creationStack;
22#endif
23
24 /// <summary>
25 /// MSI handle destructor.
26 /// </summary>
27 ~MsiHandle()
28 {
29 this.Dispose(false);
30 }
31
32 /// <summary>
33 /// Gets or sets the MSI handle.
34 /// </summary>
35 /// <value>The MSI handle.</value>
36 internal uint Handle
37 {
38 get
39 {
40 if (this.disposed)
41 {
42 throw new ObjectDisposedException("MsiHandle");
43 }
44
45 return this.handle;
46 }
47
48 set
49 {
50 if (this.disposed)
51 {
52 throw new ObjectDisposedException("MsiHandle");
53 }
54
55 this.handle = value;
56 this.owningThread = Thread.CurrentThread.ManagedThreadId;
57#if DEBUG
58 this.creationStack = Environment.StackTrace;
59#endif
60 }
61 }
62
63 /// <summary>
64 /// Close the MSI handle.
65 /// </summary>
66 public void Close()
67 {
68 this.Dispose();
69 }
70
71 /// <summary>
72 /// Disposes the managed and unmanaged objects in this object.
73 /// </summary>
74 public void Dispose()
75 {
76 this.Dispose(true);
77 GC.SuppressFinalize(this);
78 }
79
80 /// <summary>
81 /// Disposes the managed and unmanaged objects in this object.
82 /// </summary>
83 /// <param name="disposing">true to dispose the managed objects.</param>
84 protected virtual void Dispose(bool disposing)
85 {
86 if (!this.disposed)
87 {
88 if (0 != this.handle)
89 {
90 if (Thread.CurrentThread.ManagedThreadId == this.owningThread)
91 {
92 int error = MsiInterop.MsiCloseHandle(this.handle);
93 if (0 != error)
94 {
95 throw new Win32Exception(error);
96 }
97 this.handle = 0;
98 }
99 else
100 {
101 // Don't try to close the handle on a different thread than it was opened.
102 // This will occasionally cause MSI to AV.
103 string message = String.Format("Leaked msi handle {0} created on thread {1} by type {2}. This handle cannot be closed on thread {3}",
104 this.handle, this.owningThread, this.GetType(), Thread.CurrentThread.ManagedThreadId);
105#if DEBUG
106 throw new InvalidOperationException(String.Format("{0}. Created {1}", message, this.creationStack));
107#else
108 Debug.WriteLine(message);
109#endif
110 }
111 }
112
113 this.disposed = true;
114 }
115 }
116 }
117}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/MsiInterop.cs b/src/WixToolset.Core.WindowsInstaller/Msi/MsiInterop.cs
deleted file mode 100644
index ae585612..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/MsiInterop.cs
+++ /dev/null
@@ -1,571 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Text;
7 using System.Runtime.InteropServices;
8 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
9 using WixToolset.Core.Native;
10
11 /// <summary>
12 /// A callback function that the installer calls for progress notification and error messages.
13 /// </summary>
14 /// <param name="context">Pointer to an application context.
15 /// This parameter can be used for error checking.</param>
16 /// <param name="messageType">Specifies a combination of one message box style,
17 /// one message box icon type, one default button, and one installation message type.</param>
18 /// <param name="message">Specifies the message text.</param>
19 /// <returns>-1 for an error, 0 if no action was taken, 1 if OK, 3 to abort.</returns>
20 public delegate int InstallUIHandler(IntPtr context, uint messageType, [MarshalAs(UnmanagedType.LPWStr)] string message);
21
22 /// <summary>
23 /// Enum of predefined persist modes used when opening a database.
24 /// </summary>
25 public enum OpenDatabase
26 {
27 /// <summary>
28 /// Open a database read-only, no persistent changes.
29 /// </summary>
30 ReadOnly = 0,
31
32 /// <summary>
33 /// Open a database read/write in transaction mode.
34 /// </summary>
35 Transact = 1,
36
37 /// <summary>
38 /// Open a database direct read/write without transaction.
39 /// </summary>
40 Direct = 2,
41
42 /// <summary>
43 /// Create a new database, transact mode read/write.
44 /// </summary>
45 Create = 3,
46
47 /// <summary>
48 /// Create a new database, direct mode read/write.
49 /// </summary>
50 CreateDirect = 4,
51
52 /// <summary>
53 /// Indicates a patch file is being opened.
54 /// </summary>
55 OpenPatchFile = 32
56 }
57
58 /// <summary>
59 /// The errors to suppress when applying a transform.
60 /// </summary>
61 [Flags]
62 public enum TransformErrorConditions
63 {
64 /// <summary>
65 /// None of the following conditions.
66 /// </summary>
67 None = 0x0,
68
69 /// <summary>
70 /// Suppress error when adding a row that exists.
71 /// </summary>
72 AddExistingRow = 0x1,
73
74 /// <summary>
75 /// Suppress error when deleting a row that does not exist.
76 /// </summary>
77 DeleteMissingRow = 0x2,
78
79 /// <summary>
80 /// Suppress error when adding a table that exists.
81 /// </summary>
82 AddExistingTable = 0x4,
83
84 /// <summary>
85 /// Suppress error when deleting a table that does not exist.
86 /// </summary>
87 DeleteMissingTable = 0x8,
88
89 /// <summary>
90 /// Suppress error when updating a row that does not exist.
91 /// </summary>
92 UpdateMissingRow = 0x10,
93
94 /// <summary>
95 /// Suppress error when transform and database code pages do not match, and their code pages are neutral.
96 /// </summary>
97 ChangeCodepage = 0x20,
98
99 /// <summary>
100 /// Create the temporary _TransformView table when applying a transform.
101 /// </summary>
102 ViewTransform = 0x100,
103
104 /// <summary>
105 /// Suppress all errors but the option to create the temporary _TransformView table.
106 /// </summary>
107 All = 0x3F
108 }
109
110 /// <summary>
111 /// The validation to run while applying a transform.
112 /// </summary>
113 [Flags]
114 public enum TransformValidations
115 {
116 /// <summary>
117 /// Do not validate properties.
118 /// </summary>
119 None = 0x0,
120
121 /// <summary>
122 /// Default language must match base database.
123 /// </summary>
124 Language = 0x1,
125
126 /// <summary>
127 /// Product must match base database.
128 /// </summary>
129 Product = 0x2,
130
131 /// <summary>
132 /// Check major version only.
133 /// </summary>
134 MajorVersion = 0x8,
135
136 /// <summary>
137 /// Check major and minor versions only.
138 /// </summary>
139 MinorVersion = 0x10,
140
141 /// <summary>
142 /// Check major, minor, and update versions.
143 /// </summary>
144 UpdateVersion = 0x20,
145
146 /// <summary>
147 /// Installed version &lt; base version.
148 /// </summary>
149 NewLessBaseVersion = 0x40,
150
151 /// <summary>
152 /// Installed version &lt;= base version.
153 /// </summary>
154 NewLessEqualBaseVersion = 0x80,
155
156 /// <summary>
157 /// Installed version = base version.
158 /// </summary>
159 NewEqualBaseVersion = 0x100,
160
161 /// <summary>
162 /// Installed version &gt;= base version.
163 /// </summary>
164 NewGreaterEqualBaseVersion = 0x200,
165
166 /// <summary>
167 /// Installed version &gt; base version.
168 /// </summary>
169 NewGreaterBaseVersion = 0x400,
170
171 /// <summary>
172 /// UpgradeCode must match base database.
173 /// </summary>
174 UpgradeCode = 0x800
175 }
176
177 /// <summary>
178 /// Class exposing static functions and structs from MSI API.
179 /// </summary>
180 internal sealed class MsiInterop
181 {
182 // Patching constants
183 public const int MsiMaxStreamNameLength = 62; // http://msdn2.microsoft.com/library/aa370551.aspx
184
185 public const int MSICONDITIONFALSE = 0; // The table is temporary.
186 public const int MSICONDITIONTRUE = 1; // The table is persistent.
187 public const int MSICONDITIONNONE = 2; // The table is unknown.
188 public const int MSICONDITIONERROR = 3; // An invalid handle or invalid parameter was passed to the function.
189 /*
190 public const int MSIDBOPENREADONLY = 0;
191 public const int MSIDBOPENTRANSACT = 1;
192 public const int MSIDBOPENDIRECT = 2;
193 public const int MSIDBOPENCREATE = 3;
194 public const int MSIDBOPENCREATEDIRECT = 4;
195 public const int MSIDBOPENPATCHFILE = 32;
196
197 public const int MSIMODIFYSEEK = -1; // Refreshes the information in the supplied record without changing the position in the result set and without affecting subsequent fetch operations. The record may then be used for subsequent Update, Delete, and Refresh. All primary key columns of the table must be in the query and the record must have at least as many fields as the query. Seek cannot be used with multi-table queries. This mode cannot be used with a view containing joins. See also the remarks.
198 public const int MSIMODIFYREFRESH = 0; // Refreshes the information in the record. Must first call MsiViewFetch with the same record. Fails for a deleted row. Works with read-write and read-only records.
199 public const int MSIMODIFYINSERT = 1; // Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only database. This mode cannot be used with a view containing joins.
200 public const int MSIMODIFYUPDATE = 2; // Updates an existing record. Nonprimary keys only. Must first call MsiViewFetch. Fails with a deleted record. Works only with read-write records.
201 public const int MSIMODIFYASSIGN = 3; // Writes current data in the cursor to a table row. Updates record if the primary keys match an existing row and inserts if they do not match. Fails with a read-only database. This mode cannot be used with a view containing joins.
202 public const int MSIMODIFYREPLACE = 4; // Updates or deletes and inserts a record into a table. Must first call MsiViewFetch with the same record. Updates record if the primary keys are unchanged. Deletes old row and inserts new if primary keys have changed. Fails with a read-only database. This mode cannot be used with a view containing joins.
203 public const int MSIMODIFYMERGE = 5; // Inserts or validates a record in a table. Inserts if primary keys do not match any row and validates if there is a match. Fails if the record does not match the data in the table. Fails if there is a record with a duplicate key that is not identical. Works only with read-write records. This mode cannot be used with a view containing joins.
204 public const int MSIMODIFYDELETE = 6; // Remove a row from the table. You must first call the MsiViewFetch function with the same record. Fails if the row has been deleted. Works only with read-write records. This mode cannot be used with a view containing joins.
205 public const int MSIMODIFYINSERTTEMPORARY = 7; // Inserts a temporary record. The information is not persistent. Fails if a row with the same primary key exists. Works only with read-write records. This mode cannot be used with a view containing joins.
206 public const int MSIMODIFYVALIDATE = 8; // Validates a record. Does not validate across joins. You must first call the MsiViewFetch function with the same record. Obtain validation errors with MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
207 public const int MSIMODIFYVALIDATENEW = 9; // Validate a new record. Does not validate across joins. Checks for duplicate keys. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
208 public const int MSIMODIFYVALIDATEFIELD = 10; // Validates fields of a fetched or new record. Can validate one or more fields of an incomplete record. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
209 public const int MSIMODIFYVALIDATEDELETE = 11; // Validates a record that will be deleted later. You must first call MsiViewFetch. Fails if another row refers to the primary keys of this row. Validation does not check for the existence of the primary keys of this row in properties or strings. Does not check if a column is a foreign key to multiple tables. Obtain validation errors by calling MsiViewGetError. Works with read-write and read-only records. This mode cannot be used with a view containing joins.
210
211 public const uint VTI2 = 2;
212 public const uint VTI4 = 3;
213 public const uint VTLPWSTR = 30;
214 public const uint VTFILETIME = 64;
215 */
216
217 public const int MSICOLINFONAMES = 0; // return column names
218 public const int MSICOLINFOTYPES = 1; // return column definitions, datatype code followed by width
219
220 /// <summary>
221 /// Protect the constructor.
222 /// </summary>
223 private MsiInterop()
224 {
225 }
226
227 /// <summary>
228 /// PInvoke of MsiCloseHandle.
229 /// </summary>
230 /// <param name="database">Handle to a database.</param>
231 /// <returns>Error code.</returns>
232 [DllImport("msi.dll", EntryPoint = "MsiCloseHandle", CharSet = CharSet.Unicode, ExactSpelling = true)]
233 public static extern int MsiCloseHandle(uint database);
234
235 /// <summary>
236 /// PInvoke of MsiCreateRecord
237 /// </summary>
238 /// <param name="parameters">Count of columns in the record.</param>
239 /// <returns>Handle referencing the record.</returns>
240 [DllImport("msi.dll", EntryPoint = "MsiCreateRecord", CharSet = CharSet.Unicode, ExactSpelling = true)]
241 public static extern uint MsiCreateRecord(int parameters);
242
243 /// <summary>
244 /// Creates summary information of an existing transform to include validation and error conditions.
245 /// </summary>
246 /// <param name="database">The handle to the database that contains the new database summary information.</param>
247 /// <param name="referenceDatabase">The handle to the database that contains the original summary information.</param>
248 /// <param name="transformFile">The name of the transform to which the summary information is added.</param>
249 /// <param name="errorConditions">The error conditions that should be suppressed when the transform is applied.</param>
250 /// <param name="validations">Specifies the properties to be validated to verify that the transform can be applied to the database.</param>
251 /// <returns>Error code.</returns>
252 [DllImport("msi.dll", EntryPoint = "MsiCreateTransformSummaryInfoW", CharSet = CharSet.Unicode, ExactSpelling = true)]
253 public static extern int MsiCreateTransformSummaryInfo(uint database, uint referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations);
254
255 /// <summary>
256 /// Applies a transform to a database.
257 /// </summary>
258 /// <param name="database">Handle to the database obtained from MsiOpenDatabase to transform.</param>
259 /// <param name="transformFile">Specifies the name of the transform file to apply.</param>
260 /// <param name="errorConditions">Error conditions that should be suppressed.</param>
261 /// <returns>Error code.</returns>
262 [DllImport("msi.dll", EntryPoint = "MsiDatabaseApplyTransformW", CharSet = CharSet.Unicode, ExactSpelling = true)]
263 public static extern int MsiDatabaseApplyTransform(uint database, string transformFile, TransformErrorConditions errorConditions);
264
265 /// <summary>
266 /// PInvoke of MsiDatabaseCommit.
267 /// </summary>
268 /// <param name="database">Handle to a databse.</param>
269 /// <returns>Error code.</returns>
270 [DllImport("msi.dll", EntryPoint = "MsiDatabaseCommit", CharSet = CharSet.Unicode, ExactSpelling = true)]
271 public static extern int MsiDatabaseCommit(uint database);
272
273 /// <summary>
274 /// PInvoke of MsiDatabaseExportW.
275 /// </summary>
276 /// <param name="database">Handle to a database.</param>
277 /// <param name="tableName">Table name.</param>
278 /// <param name="folderPath">Folder path.</param>
279 /// <param name="fileName">File name.</param>
280 /// <returns>Error code.</returns>
281 [DllImport("msi.dll", EntryPoint = "MsiDatabaseExportW", CharSet = CharSet.Unicode, ExactSpelling = true)]
282 public static extern int MsiDatabaseExport(uint database, string tableName, string folderPath, string fileName);
283
284 /// <summary>
285 /// Generates a transform file of differences between two databases.
286 /// </summary>
287 /// <param name="database">Handle to the database obtained from MsiOpenDatabase that includes the changes.</param>
288 /// <param name="databaseReference">Handle to the database obtained from MsiOpenDatabase that does not include the changes.</param>
289 /// <param name="transformFile">A null-terminated string that specifies the name of the transform file being generated.
290 /// This parameter can be null. If szTransformFile is null, you can use MsiDatabaseGenerateTransform to test whether two
291 /// databases are identical without creating a transform. If the databases are identical, the function returns ERROR_NO_DATA.
292 /// If the databases are different the function returns NOERROR.</param>
293 /// <param name="reserved1">This is a reserved argument and must be set to 0.</param>
294 /// <param name="reserved2">This is a reserved argument and must be set to 0.</param>
295 /// <returns>Error code.</returns>
296 [DllImport("msi.dll", EntryPoint = "MsiDatabaseGenerateTransformW", CharSet = CharSet.Unicode, ExactSpelling = true)]
297 public static extern int MsiDatabaseGenerateTransform(uint database, uint databaseReference, string transformFile, int reserved1, int reserved2);
298
299 /// <summary>
300 /// PInvoke of MsiDatabaseImportW.
301 /// </summary>
302 /// <param name="database">Handle to a database.</param>
303 /// <param name="folderPath">Folder path.</param>
304 /// <param name="fileName">File name.</param>
305 /// <returns>Error code.</returns>
306 [DllImport("msi.dll", EntryPoint = "MsiDatabaseImportW", CharSet = CharSet.Unicode, ExactSpelling = true)]
307 public static extern int MsiDatabaseImport(uint database, string folderPath, string fileName);
308
309 /// <summary>
310 /// PInvoke of MsiDatabaseMergeW.
311 /// </summary>
312 /// <param name="database">The handle to the database obtained from MsiOpenDatabase.</param>
313 /// <param name="databaseMerge">The handle to the database obtained from MsiOpenDatabase to merge into the base database.</param>
314 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
315 /// <returns>Error code.</returns>
316 [DllImport("msi.dll", EntryPoint = "MsiDatabaseMergeW", CharSet = CharSet.Unicode, ExactSpelling = true)]
317 public static extern int MsiDatabaseMerge(uint database, uint databaseMerge, string tableName);
318
319 /// <summary>
320 /// PInvoke of MsiDatabaseOpenViewW.
321 /// </summary>
322 /// <param name="database">Handle to a database.</param>
323 /// <param name="query">SQL query.</param>
324 /// <param name="view">View handle.</param>
325 /// <returns>Error code.</returns>
326 [DllImport("msi.dll", EntryPoint = "MsiDatabaseOpenViewW", CharSet = CharSet.Unicode, ExactSpelling = true)]
327 public static extern int MsiDatabaseOpenView(uint database, string query, out uint view);
328
329 /// <summary>
330 /// PInvoke of MsiGetFileHashW.
331 /// </summary>
332 /// <param name="filePath">File path.</param>
333 /// <param name="options">Hash options (must be 0).</param>
334 /// <param name="hash">Buffer to recieve hash.</param>
335 /// <returns>Error code.</returns>
336 [DllImport("msi.dll", EntryPoint = "MsiGetFileHashW", CharSet = CharSet.Unicode, ExactSpelling = true)]
337 public static extern int MsiGetFileHash(string filePath, uint options, MSIFILEHASHINFO hash);
338
339 /// <summary>
340 /// PInvoke of MsiGetFileVersionW.
341 /// </summary>
342 /// <param name="filePath">File path.</param>
343 /// <param name="versionBuf">Buffer to receive version info.</param>
344 /// <param name="versionBufSize">Size of version buffer.</param>
345 /// <param name="langBuf">Buffer to recieve lang info.</param>
346 /// <param name="langBufSize">Size of lang buffer.</param>
347 /// <returns>Error code.</returns>
348 [DllImport("msi.dll", EntryPoint = "MsiGetFileVersionW", CharSet = CharSet.Unicode, ExactSpelling = true)]
349 public static extern int MsiGetFileVersion(string filePath, StringBuilder versionBuf, ref int versionBufSize, StringBuilder langBuf, ref int langBufSize);
350
351 /// <summary>
352 /// PInvoke of MsiGetLastErrorRecord.
353 /// </summary>
354 /// <returns>Handle to error record if one exists.</returns>
355 [DllImport("msi.dll", EntryPoint = "MsiGetLastErrorRecord", CharSet = CharSet.Unicode, ExactSpelling = true)]
356 public static extern uint MsiGetLastErrorRecord();
357
358 /// <summary>
359 /// PInvoke of MsiDatabaseGetPrimaryKeysW.
360 /// </summary>
361 /// <param name="database">Handle to a database.</param>
362 /// <param name="tableName">Table name.</param>
363 /// <param name="record">Handle to receive resulting record.</param>
364 /// <returns>Error code.</returns>
365 [DllImport("msi.dll", EntryPoint = "MsiDatabaseGetPrimaryKeysW", CharSet = CharSet.Unicode, ExactSpelling = true)]
366 public static extern int MsiDatabaseGetPrimaryKeys(uint database, string tableName, out uint record);
367
368 /// <summary>
369 /// PInvoke of MsiDoActionW.
370 /// </summary>
371 /// <param name="product">Handle to the installation provided to a DLL custom action or
372 /// obtained through MsiOpenPackage, MsiOpenPackageEx, or MsiOpenProduct.</param>
373 /// <param name="action">Specifies the action to execute.</param>
374 /// <returns>Error code.</returns>
375 [DllImport("msi.dll", EntryPoint = "MsiDoActionW", CharSet = CharSet.Unicode, ExactSpelling = true)]
376 public static extern int MsiDoAction(uint product, string action);
377
378 /// <summary>
379 /// PInvoke of MsiGetSummaryInformationW. Can use either database handle or database path as input.
380 /// </summary>
381 /// <param name="database">Handle to a database.</param>
382 /// <param name="databasePath">Path to a database.</param>
383 /// <param name="updateCount">Max number of updated values.</param>
384 /// <param name="summaryInfo">Handle to summary information.</param>
385 /// <returns>Error code.</returns>
386 [DllImport("msi.dll", EntryPoint = "MsiGetSummaryInformationW", CharSet = CharSet.Unicode, ExactSpelling = true)]
387 public static extern int MsiGetSummaryInformation(uint database, string databasePath, uint updateCount, ref uint summaryInfo);
388
389 /// <summary>
390 /// PInvoke of MsiDatabaseIsTablePersitentW.
391 /// </summary>
392 /// <param name="database">Handle to a database.</param>
393 /// <param name="tableName">Table name.</param>
394 /// <returns>MSICONDITION</returns>
395 [DllImport("msi.dll", EntryPoint = "MsiDatabaseIsTablePersistentW", CharSet = CharSet.Unicode, ExactSpelling = true)]
396 public static extern int MsiDatabaseIsTablePersistent(uint database, string tableName);
397
398 /// <summary>
399 /// PInvoke of MsiOpenDatabaseW.
400 /// </summary>
401 /// <param name="databasePath">Path to database.</param>
402 /// <param name="persist">Persist mode.</param>
403 /// <param name="database">Handle to database.</param>
404 /// <returns>Error code.</returns>
405 [DllImport("msi.dll", EntryPoint = "MsiOpenDatabaseW", CharSet = CharSet.Unicode, ExactSpelling = true)]
406 public static extern int MsiOpenDatabase(string databasePath, IntPtr persist, out uint database);
407
408 /// <summary>
409 /// PInvoke of MsiOpenPackageW.
410 /// </summary>
411 /// <param name="packagePath">The path to the package.</param>
412 /// <param name="product">A pointer to a variable that receives the product handle.</param>
413 /// <returns>Error code.</returns>
414 [DllImport("msi.dll", EntryPoint = "MsiOpenPackageW", CharSet = CharSet.Unicode, ExactSpelling = true)]
415 public static extern int MsiOpenPackage(string packagePath, out uint product);
416
417 /// <summary>
418 /// PInvoke of MsiRecordIsNull.
419 /// </summary>
420 /// <param name="record">MSI Record handle.</param>
421 /// <param name="field">Index of field to check for null value.</param>
422 /// <returns>true if the field is null, false if not, and an error code for any error.</returns>
423 [DllImport("msi.dll", EntryPoint = "MsiRecordIsNull", CharSet = CharSet.Unicode, ExactSpelling = true)]
424 public static extern int MsiRecordIsNull(uint record, int field);
425
426 /// <summary>
427 /// PInvoke of MsiRecordGetInteger.
428 /// </summary>
429 /// <param name="record">MSI Record handle.</param>
430 /// <param name="field">Index of field to retrieve integer from.</param>
431 /// <returns>Integer value.</returns>
432 [DllImport("msi.dll", EntryPoint = "MsiRecordGetInteger", CharSet = CharSet.Unicode, ExactSpelling = true)]
433 public static extern int MsiRecordGetInteger(uint record, int field);
434
435 /// <summary>
436 /// PInvoke of MsiRectordSetInteger.
437 /// </summary>
438 /// <param name="record">MSI Record handle.</param>
439 /// <param name="field">Index of field to set integer value in.</param>
440 /// <param name="value">Value to set field to.</param>
441 /// <returns>Error code.</returns>
442 [DllImport("msi.dll", EntryPoint = "MsiRecordSetInteger", CharSet = CharSet.Unicode, ExactSpelling = true)]
443 public static extern int MsiRecordSetInteger(uint record, int field, int value);
444
445 /// <summary>
446 /// PInvoke of MsiRecordGetStringW.
447 /// </summary>
448 /// <param name="record">MSI Record handle.</param>
449 /// <param name="field">Index of field to get string value from.</param>
450 /// <param name="valueBuf">Buffer to recieve value.</param>
451 /// <param name="valueBufSize">Size of buffer.</param>
452 /// <returns>Error code.</returns>
453 [DllImport("msi.dll", EntryPoint = "MsiRecordGetStringW", CharSet = CharSet.Unicode, ExactSpelling = true)]
454 public static extern int MsiRecordGetString(uint record, int field, StringBuilder valueBuf, ref int valueBufSize);
455
456 /// <summary>
457 /// PInvoke of MsiRecordSetStringW.
458 /// </summary>
459 /// <param name="record">MSI Record handle.</param>
460 /// <param name="field">Index of field to set string value in.</param>
461 /// <param name="value">String value.</param>
462 /// <returns>Error code.</returns>
463 [DllImport("msi.dll", EntryPoint = "MsiRecordSetStringW", CharSet = CharSet.Unicode, ExactSpelling = true)]
464 public static extern int MsiRecordSetString(uint record, int field, string value);
465
466 /// <summary>
467 /// PInvoke of MsiRecordSetStreamW.
468 /// </summary>
469 /// <param name="record">MSI Record handle.</param>
470 /// <param name="field">Index of field to set stream value in.</param>
471 /// <param name="filePath">Path to file to set stream value to.</param>
472 /// <returns>Error code.</returns>
473 [DllImport("msi.dll", EntryPoint = "MsiRecordSetStreamW", CharSet = CharSet.Unicode, ExactSpelling = true)]
474 public static extern int MsiRecordSetStream(uint record, int field, string filePath);
475
476 /// <summary>
477 /// PInvoke of MsiRecordReadStreamW.
478 /// </summary>
479 /// <param name="record">MSI Record handle.</param>
480 /// <param name="field">Index of field to read stream from.</param>
481 /// <param name="dataBuf">Data buffer to recieve stream value.</param>
482 /// <param name="dataBufSize">Size of data buffer.</param>
483 /// <returns>Error code.</returns>
484 [DllImport("msi.dll", EntryPoint = "MsiRecordReadStream", CharSet = CharSet.Unicode, ExactSpelling = true)]
485 public static extern int MsiRecordReadStream(uint record, int field, byte[] dataBuf, ref int dataBufSize);
486
487 /// <summary>
488 /// PInvoke of MsiRecordGetFieldCount.
489 /// </summary>
490 /// <param name="record">MSI Record handle.</param>
491 /// <returns>Count of fields in the record.</returns>
492 [DllImport("msi.dll", EntryPoint = "MsiRecordGetFieldCount", CharSet = CharSet.Unicode, ExactSpelling = true)]
493 public static extern int MsiRecordGetFieldCount(uint record);
494
495 /// <summary>
496 /// PInvoke of MsiSetExternalUIW.
497 /// </summary>
498 /// <param name="installUIHandler">Specifies a callback function that conforms to the INSTALLUI_HANDLER specification.</param>
499 /// <param name="installLogMode">Specifies which messages to handle using the external message handler. If the external
500 /// handler returns a non-zero result, then that message will not be sent to the UI, instead the message will be logged
501 /// if logging has been enabled.</param>
502 /// <param name="context">Pointer to an application context that is passed to the callback function.
503 /// This parameter can be used for error checking.</param>
504 /// <returns>The return value is the previously set external handler, or zero (0) if there was no previously set handler.</returns>
505 [DllImport("msi.dll", EntryPoint = "MsiSetExternalUIW", CharSet = CharSet.Unicode, ExactSpelling = true)]
506 public static extern InstallUIHandler MsiSetExternalUI(InstallUIHandler installUIHandler, int installLogMode, IntPtr context);
507
508 /// <summary>
509 /// PInvoke of MsiSetpublicUI.
510 /// </summary>
511 /// <param name="uiLevel">Specifies the level of complexity of the user interface.</param>
512 /// <param name="hwnd">Pointer to a window. This window becomes the owner of any user interface created.
513 /// A pointer to the previous owner of the user interface is returned.
514 /// If this parameter is null, the owner of the user interface does not change.</param>
515 /// <returns>The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned.</returns>
516 [DllImport("msi.dll", EntryPoint = "MsiSetpublicUI", CharSet = CharSet.Unicode, ExactSpelling = true)]
517 public static extern int MsiSetInternalUI(int uiLevel, ref IntPtr hwnd);
518
519 /// <summary>
520 /// PInvoke of MsiSummaryInfoGetPropertyW.
521 /// </summary>
522 /// <param name="summaryInfo">Handle to summary info.</param>
523 /// <param name="property">Property to get value from.</param>
524 /// <param name="dataType">Data type of property.</param>
525 /// <param name="integerValue">Integer to receive integer value.</param>
526 /// <param name="fileTimeValue">File time to receive file time value.</param>
527 /// <param name="stringValueBuf">String buffer to receive string value.</param>
528 /// <param name="stringValueBufSize">Size of string buffer.</param>
529 /// <returns>Error code.</returns>
530 [DllImport("msi.dll", EntryPoint = "MsiSummaryInfoGetPropertyW", CharSet = CharSet.Unicode, ExactSpelling = true)]
531 public static extern int MsiSummaryInfoGetProperty(uint summaryInfo, int property, out uint dataType, out int integerValue, ref FILETIME fileTimeValue, StringBuilder stringValueBuf, ref int stringValueBufSize);
532
533 /// <summary>
534 /// PInvoke of MsiViewGetColumnInfo.
535 /// </summary>
536 /// <param name="view">Handle to view.</param>
537 /// <param name="columnInfo">Column info.</param>
538 /// <param name="record">Handle for returned record.</param>
539 /// <returns>Error code.</returns>
540 [DllImport("msi.dll", EntryPoint = "MsiViewGetColumnInfo", CharSet = CharSet.Unicode, ExactSpelling = true)]
541 public static extern int MsiViewGetColumnInfo(uint view, int columnInfo, out uint record);
542
543 /// <summary>
544 /// PInvoke of MsiViewExecute.
545 /// </summary>
546 /// <param name="view">Handle of view to execute.</param>
547 /// <param name="record">Handle to a record that supplies the parameters for the view.</param>
548 /// <returns>Error code.</returns>
549 [DllImport("msi.dll", EntryPoint = "MsiViewExecute", CharSet = CharSet.Unicode, ExactSpelling = true)]
550 public static extern int MsiViewExecute(uint view, uint record);
551
552 /// <summary>
553 /// PInvoke of MsiViewFetch.
554 /// </summary>
555 /// <param name="view">Handle of view to fetch a row from.</param>
556 /// <param name="record">Handle to receive record info.</param>
557 /// <returns>Error code.</returns>
558 [DllImport("msi.dll", EntryPoint = "MsiViewFetch", CharSet = CharSet.Unicode, ExactSpelling = true)]
559 public static extern int MsiViewFetch(uint view, out uint record);
560
561 /// <summary>
562 /// PInvoke of MsiViewModify.
563 /// </summary>
564 /// <param name="view">Handle of view to modify.</param>
565 /// <param name="modifyMode">Modify mode.</param>
566 /// <param name="record">Handle of record.</param>
567 /// <returns>Error code.</returns>
568 [DllImport("msi.dll", EntryPoint = "MsiViewModify", CharSet = CharSet.Unicode, ExactSpelling = true)]
569 public static extern int MsiViewModify(uint view, int modifyMode, uint record);
570 }
571}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
deleted file mode 100644
index 7342659b..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/Record.cs
+++ /dev/null
@@ -1,181 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.ComponentModel;
7 using System.Text;
8
9 /// <summary>
10 /// Wrapper class around msi.dll interop for a record.
11 /// </summary>
12 public sealed class Record : MsiHandle
13 {
14 /// <summary>
15 /// Creates a record with the specified number of fields.
16 /// </summary>
17 /// <param name="fieldCount">Number of fields in record.</param>
18 public Record(int fieldCount)
19 {
20 this.Handle = MsiInterop.MsiCreateRecord(fieldCount);
21 if (0 == this.Handle)
22 {
23 throw new OutOfMemoryException();
24 }
25 }
26
27 /// <summary>
28 /// Creates a record from a handle.
29 /// </summary>
30 /// <param name="handle">Handle to create record from.</param>
31 internal Record(uint handle)
32 {
33 this.Handle = handle;
34 }
35
36 /// <summary>
37 /// Gets a string value at specified location.
38 /// </summary>
39 /// <param name="field">Index into record to get string.</param>
40 public string this[int field]
41 {
42 get { return this.GetString(field); }
43 set { this.SetString(field, (string)value); }
44 }
45
46 /// <summary>
47 /// Determines if the value is null at the specified location.
48 /// </summary>
49 /// <param name="field">Index into record of the field to query.</param>
50 /// <returns>true if the value is null, false otherwise.</returns>
51 public bool IsNull(int field)
52 {
53 int error = MsiInterop.MsiRecordIsNull(this.Handle, field);
54
55 switch (error)
56 {
57 case 0:
58 return false;
59 case 1:
60 return true;
61 default:
62 throw new Win32Exception(error);
63 }
64 }
65
66 /// <summary>
67 /// Gets integer value at specified location.
68 /// </summary>
69 /// <param name="field">Index into record to get integer</param>
70 /// <returns>Integer value</returns>
71 public int GetInteger(int field)
72 {
73 return MsiInterop.MsiRecordGetInteger(this.Handle, field);
74 }
75
76 /// <summary>
77 /// Sets integer value at specified location.
78 /// </summary>
79 /// <param name="field">Index into record to set integer.</param>
80 /// <param name="value">Value to set into record.</param>
81 public void SetInteger(int field, int value)
82 {
83 int error = MsiInterop.MsiRecordSetInteger(this.Handle, field, value);
84 if (0 != error)
85 {
86 throw new Win32Exception(error);
87 }
88 }
89
90 /// <summary>
91 /// Gets string value at specified location.
92 /// </summary>
93 /// <param name="field">Index into record to get string.</param>
94 /// <returns>String value</returns>
95 public string GetString(int field)
96 {
97 int bufferSize = 255;
98 StringBuilder buffer = new StringBuilder(bufferSize);
99 int error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize);
100 if (234 == error)
101 {
102 buffer.EnsureCapacity(++bufferSize);
103 error = MsiInterop.MsiRecordGetString(this.Handle, field, buffer, ref bufferSize);
104 }
105
106 if (0 != error)
107 {
108 throw new Win32Exception(error);
109 }
110
111 return (0 < buffer.Length ? buffer.ToString() : null);
112 }
113
114 /// <summary>
115 /// Set string value at specified location
116 /// </summary>
117 /// <param name="field">Index into record to set string.</param>
118 /// <param name="value">Value to set into record</param>
119 public void SetString(int field, string value)
120 {
121 int error = MsiInterop.MsiRecordSetString(this.Handle, field, value);
122 if (0 != error)
123 {
124 throw new Win32Exception(error);
125 }
126 }
127
128 /// <summary>
129 /// Get stream at specified location.
130 /// </summary>
131 /// <param name="field">Index into record to get stream.</param>
132 /// <param name="buffer">buffer to receive bytes from stream.</param>
133 /// <param name="requestedBufferSize">Buffer size to read.</param>
134 /// <returns>Stream read into string.</returns>
135 public int GetStream(int field, byte[] buffer, int requestedBufferSize)
136 {
137 int bufferSize = 255;
138 if (requestedBufferSize > 0)
139 {
140 bufferSize = requestedBufferSize;
141 }
142
143 int error = MsiInterop.MsiRecordReadStream(this.Handle, field, buffer, ref bufferSize);
144 if (0 != error)
145 {
146 throw new Win32Exception(error);
147 }
148
149 return bufferSize;
150 }
151
152 /// <summary>
153 /// Sets a stream at a specified location.
154 /// </summary>
155 /// <param name="field">Index into record to set stream.</param>
156 /// <param name="path">Path to file to read into stream.</param>
157 public void SetStream(int field, string path)
158 {
159 int error = MsiInterop.MsiRecordSetStream(this.Handle, field, path);
160 if (0 != error)
161 {
162 throw new Win32Exception(error);
163 }
164 }
165
166 /// <summary>
167 /// Gets the number of fields in record.
168 /// </summary>
169 /// <returns>Count of fields in record.</returns>
170 public int GetFieldCount()
171 {
172 int size = MsiInterop.MsiRecordGetFieldCount(this.Handle);
173 if (0 > size)
174 {
175 throw new Win32Exception();
176 }
177
178 return size;
179 }
180 }
181}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs b/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
deleted file mode 100644
index bb07a501..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/Session.cs
+++ /dev/null
@@ -1,43 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Globalization;
7
8 /// <summary>
9 /// Controls the installation process.
10 /// </summary>
11 internal sealed class Session : MsiHandle
12 {
13 /// <summary>
14 /// Instantiate a new Session.
15 /// </summary>
16 /// <param name="database">The database to open.</param>
17 public Session(Database database)
18 {
19 string packagePath = String.Format(CultureInfo.InvariantCulture, "#{0}", (uint)database.Handle);
20
21 uint handle = 0;
22 int error = MsiInterop.MsiOpenPackage(packagePath, out handle);
23 if (0 != error)
24 {
25 throw new MsiException(error);
26 }
27 this.Handle = handle;
28 }
29
30 /// <summary>
31 /// Executes a built-in action, custom action, or user-interface wizard action.
32 /// </summary>
33 /// <param name="action">Specifies the action to execute.</param>
34 public void DoAction(string action)
35 {
36 int error = MsiInterop.MsiDoAction(this.Handle, action);
37 if (0 != error)
38 {
39 throw new MsiException(error);
40 }
41 }
42 }
43}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs b/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
deleted file mode 100644
index 5450671f..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/SummaryInformation.cs
+++ /dev/null
@@ -1,244 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Globalization;
7 using System.Text;
8 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
9
10 /// <summary>
11 /// Summary information for the MSI files.
12 /// </summary>
13 internal sealed class SummaryInformation : MsiHandle
14 {
15 /// <summary>
16 /// Summary information properties for transforms.
17 /// </summary>
18 public enum Transform
19 {
20 /// <summary>PID_CODEPAGE = code page for the summary information stream</summary>
21 CodePage = 1,
22
23 /// <summary>PID_TITLE = typically just "Transform"</summary>
24 Title = 2,
25
26 /// <summary>PID_SUBJECT = original subject of target</summary>
27 TargetSubject = 3,
28
29 /// <summary>PID_AUTHOR = original manufacturer of target</summary>
30 TargetManufacturer = 4,
31
32 /// <summary>PID_KEYWORDS = keywords for the transform, typically including at least "Installer"</summary>
33 Keywords = 5,
34
35 /// <summary>PID_COMMENTS = describes what this package does</summary>
36 Comments = 6,
37
38 /// <summary>PID_TEMPLATE = target platform;language</summary>
39 TargetPlatformAndLanguage = 7,
40
41 /// <summary>PID_LASTAUTHOR = updated platform;language</summary>
42 UpdatedPlatformAndLanguage = 8,
43
44 /// <summary>PID_REVNUMBER = {productcode}version;{newproductcode}newversion;upgradecode</summary>
45 ProductCodes = 9,
46
47 /// <summary>PID_LASTPRINTED should be null for transforms</summary>
48 Reserved11 = 11,
49
50 ///.<summary>PID_CREATE_DTM = the timestamp when the transform was created</summary>
51 CreationTime = 12,
52
53 /// <summary>PID_PAGECOUNT = minimum installer version</summary>
54 InstallerRequirement = 14,
55
56 /// <summary>PID_CHARCOUNT = validation and error flags</summary>
57 ValidationFlags = 16,
58
59 /// <summary>PID_APPNAME = the application that created the transform</summary>
60 CreatingApplication = 18,
61
62 /// <summary>PID_SECURITY = whether read-only is enforced; should always be 4 for transforms</summary>
63 Security = 19,
64 }
65
66 /// <summary>
67 /// Summary information properties for patches.
68 /// </summary>
69 public enum Patch
70 {
71 /// <summary>PID_CODEPAGE = code page of the summary information stream</summary>
72 CodePage = 1,
73
74 /// <summary>PID_TITLE = a brief description of the package type</summary>
75 Title = 2,
76
77 /// <summary>PID_SUBJECT = package name</summary>
78 PackageName = 3,
79
80 /// <summary>PID_AUTHOR = manufacturer of the patch package</summary>
81 Manufacturer = 4,
82
83 /// <summary>PID_KEYWORDS = alternate sources for the patch package</summary>
84 Sources = 5,
85
86 /// <summary>PID_COMMENTS = general purpose of the patch package</summary>
87 Comments = 6,
88
89 /// <summary>PID_TEMPLATE = semicolon delimited list of ProductCodes</summary>
90 ProductCodes = 7,
91
92 /// <summary>PID_LASTAUTHOR = semicolon delimited list of transform names</summary>
93 TransformNames = 8,
94
95 /// <summary>PID_REVNUMBER = GUID patch code</summary>
96 PatchCode = 9,
97
98 /// <summary>PID_LASTPRINTED should be null for patches</summary>
99 Reserved11 = 11,
100
101 /// <summary>PID_PAGECOUNT should be null for patches</summary>
102 Reserved14 = 14,
103
104 /// <summary>PID_WORDCOUNT = minimum installer version</summary>
105 InstallerRequirement = 15,
106
107 /// <summary>PID_CHARCOUNT should be null for patches</summary>
108 Reserved16 = 16,
109
110 /// <summary>PID_SECURITY = read-only attribute of the patch package</summary>
111 Security = 19,
112 }
113
114 /// <summary>
115 /// Summary information values for the InstallerRequirement property.
116 /// </summary>
117 public enum InstallerRequirement
118 {
119 /// <summary>Any version of the installer will do</summary>
120 Version10 = 1,
121
122 /// <summary>At least 1.2</summary>
123 Version12 = 2,
124
125 /// <summary>At least 2.0</summary>
126 Version20 = 3,
127
128 /// <summary>At least 3.0</summary>
129 Version30 = 4,
130
131 /// <summary>At least 3.1</summary>
132 Version31 = 5,
133 }
134
135 /// <summary>
136 /// Instantiate a new SummaryInformation class from an open database.
137 /// </summary>
138 /// <param name="db">Database to retrieve summary information from.</param>
139 public SummaryInformation(Database db)
140 {
141 if (null == db)
142 {
143 throw new ArgumentNullException("db");
144 }
145
146 uint handle = 0;
147 int error = MsiInterop.MsiGetSummaryInformation(db.Handle, null, 0, ref handle);
148 if (0 != error)
149 {
150 throw new MsiException(error);
151 }
152 this.Handle = handle;
153 }
154
155 /// <summary>
156 /// Instantiate a new SummaryInformation class from a database file.
157 /// </summary>
158 /// <param name="databaseFile">The database file.</param>
159 public SummaryInformation(string databaseFile)
160 {
161 if (null == databaseFile)
162 {
163 throw new ArgumentNullException("databaseFile");
164 }
165
166 uint handle = 0;
167 int error = MsiInterop.MsiGetSummaryInformation(0, databaseFile, 0, ref handle);
168 if (0 != error)
169 {
170 throw new MsiException(error);
171 }
172 this.Handle = handle;
173 }
174
175 /// <summary>
176 /// Variant types in the summary information table.
177 /// </summary>
178 private enum VT : uint
179 {
180 /// <summary>Variant has not been assigned.</summary>
181 EMPTY = 0,
182
183 /// <summary>Null variant type.</summary>
184 NULL = 1,
185
186 /// <summary>16-bit integer variant type.</summary>
187 I2 = 2,
188
189 /// <summary>32-bit integer variant type.</summary>
190 I4 = 3,
191
192 /// <summary>String variant type.</summary>
193 LPSTR = 30,
194
195 /// <summary>Date time (FILETIME, converted to Variant time) variant type.</summary>
196 FILETIME = 64,
197 }
198
199 /// <summary>
200 /// Gets a summary information property.
201 /// </summary>
202 /// <param name="index">Index of the summary information property.</param>
203 /// <returns>The summary information property.</returns>
204 public string GetProperty(int index)
205 {
206 uint dataType;
207 StringBuilder stringValue = new StringBuilder("");
208 int bufSize = 0;
209 int intValue;
210 FILETIME timeValue;
211 timeValue.dwHighDateTime = 0;
212 timeValue.dwLowDateTime = 0;
213
214 int error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
215 if (234 == error)
216 {
217 stringValue.EnsureCapacity(++bufSize);
218 error = MsiInterop.MsiSummaryInfoGetProperty(this.Handle, index, out dataType, out intValue, ref timeValue, stringValue, ref bufSize);
219 }
220
221 if (0 != error)
222 {
223 throw new MsiException(error);
224 }
225
226 switch ((VT)dataType)
227 {
228 case VT.EMPTY:
229 return String.Empty;
230 case VT.LPSTR:
231 return stringValue.ToString();
232 case VT.I2:
233 case VT.I4:
234 return Convert.ToString(intValue, CultureInfo.InvariantCulture);
235 case VT.FILETIME:
236 long longFileTime = (((long)timeValue.dwHighDateTime) << 32) | unchecked((uint)timeValue.dwLowDateTime);
237 DateTime dateTime = DateTime.FromFileTime(longFileTime);
238 return dateTime.ToString("yyyy/MM/dd HH:mm:ss", CultureInfo.InvariantCulture);
239 default:
240 throw new InvalidOperationException();
241 }
242 }
243 }
244}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/View.cs b/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
deleted file mode 100644
index 0fb7fc62..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/View.cs
+++ /dev/null
@@ -1,262 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using System.Collections;
7 using System.Collections.Generic;
8 using System.Globalization;
9
10 /// <summary>
11 /// Enumeration of different modify modes.
12 /// </summary>
13 public enum ModifyView
14 {
15 /// <summary>
16 /// Writes current data in the cursor to a table row. Updates record if the primary
17 /// keys match an existing row and inserts if they do not match. Fails with a read-only
18 /// database. This mode cannot be used with a view containing joins.
19 /// </summary>
20 Assign = 3, // Writes current data in the cursor to a table row. Updates record if the primary keys match an existing row and inserts if they do not match. Fails with a read-only database. This mode cannot be used with a view containing joins.
21
22 /// <summary>
23 /// Remove a row from the table. You must first call the Fetch function with the same
24 /// record. Fails if the row has been deleted. Works only with read-write records. This
25 /// mode cannot be used with a view containing joins.
26 /// </summary>
27 Delete = 6, // Remove a row from the table. You must first call the MsiViewFetch function with the same record. Fails if the row has been deleted. Works only with read-write records. This mode cannot be used with a view containing joins.
28
29 /// <summary>
30 /// Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only
31 /// database. This mode cannot be used with a view containing joins.
32 /// </summary>
33 Insert = 1, // Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only database. This mode cannot be used with a view containing joins.
34
35 /// <summary>
36 /// Inserts a temporary record. The information is not persistent. Fails if a row with the
37 /// same primary key exists. Works only with read-write records. This mode cannot be
38 /// used with a view containing joins.
39 /// </summary>
40 InsertTemporary = 7, // Inserts a temporary record. The information is not persistent. Fails if a row with the same primary key exists. Works only with read-write records. This mode cannot be used with a view containing joins.
41
42 /// <summary>
43 /// Inserts or validates a record in a table. Inserts if primary keys do not match any row
44 /// and validates if there is a match. Fails if the record does not match the data in
45 /// the table. Fails if there is a record with a duplicate key that is not identical.
46 /// Works only with read-write records. This mode cannot be used with a view containing joins.
47 /// </summary>
48 Merge = 5, // Inserts or validates a record in a table. Inserts if primary keys do not match any row and validates if there is a match. Fails if the record does not match the data in the table. Fails if there is a record with a duplicate key that is not identical. Works only with read-write records. This mode cannot be used with a view containing joins.
49
50 /// <summary>
51 /// Refreshes the information in the record. Must first call Fetch with the
52 /// same record. Fails for a deleted row. Works with read-write and read-only records.
53 /// </summary>
54 Refresh = 0, // Refreshes the information in the record. Must first call MsiViewFetch with the same record. Fails for a deleted row. Works with read-write and read-only records.
55
56 /// <summary>
57 /// Updates or deletes and inserts a record into a table. Must first call Fetch with
58 /// the same record. Updates record if the primary keys are unchanged. Deletes old row and
59 /// inserts new if primary keys have changed. Fails with a read-only database. This mode cannot
60 /// be used with a view containing joins.
61 /// </summary>
62 Replace = 4, // Updates or deletes and inserts a record into a table. Must first call MsiViewFetch with the same record. Updates record if the primary keys are unchanged. Deletes old row and inserts new if primary keys have changed. Fails with a read-only database. This mode cannot be used with a view containing joins.
63
64 /// <summary>
65 /// Refreshes the information in the supplied record without changing the position in the
66 /// result set and without affecting subsequent fetch operations. The record may then
67 /// be used for subsequent Update, Delete, and Refresh. All primary key columns of the
68 /// table must be in the query and the record must have at least as many fields as the
69 /// query. Seek cannot be used with multi-table queries. This mode cannot be used with
70 /// a view containing joins. See also the remarks.
71 /// </summary>
72 Seek = -1, // Refreshes the information in the supplied record without changing the position in the result set and without affecting subsequent fetch operations. The record may then be used for subsequent Update, Delete, and Refresh. All primary key columns of the table must be in the query and the record must have at least as many fields as the query. Seek cannot be used with multi-table queries. This mode cannot be used with a view containing joins. See also the remarks.
73
74 /// <summary>
75 /// Updates an existing record. Non-primary keys only. Must first call Fetch. Fails with a
76 /// deleted record. Works only with read-write records.
77 /// </summary>
78 Update = 2, // Updates an existing record. Nonprimary keys only. Must first call MsiViewFetch. Fails with a deleted record. Works only with read-write records.
79 }
80
81 /// <summary>
82 /// Wrapper class for MSI API views.
83 /// </summary>
84 internal sealed class View : MsiHandle
85 {
86 /// <summary>
87 /// Constructor that creates a view given a database handle and a query.
88 /// </summary>
89 /// <param name="db">Handle to the database to run the query on.</param>
90 /// <param name="query">Query to be executed.</param>
91 public View(Database db, string query)
92 {
93 if (null == db)
94 {
95 throw new ArgumentNullException("db");
96 }
97
98 if (null == query)
99 {
100 throw new ArgumentNullException("query");
101 }
102
103 uint handle = 0;
104
105 int error = MsiInterop.MsiDatabaseOpenView(db.Handle, query, out handle);
106 if (0 != error)
107 {
108 throw new MsiException(error);
109 }
110
111 this.Handle = handle;
112 }
113
114 /// <summary>
115 /// Enumerator that automatically disposes of the retrieved Records.
116 /// </summary>
117 public IEnumerable<Record> Records => new ViewEnumerable(this);
118
119 /// <summary>
120 /// Executes a view with no customizable parameters.
121 /// </summary>
122 public void Execute()
123 {
124 this.Execute(null);
125 }
126
127 /// <summary>
128 /// Executes a query substituing the values from the records into the customizable parameters
129 /// in the view.
130 /// </summary>
131 /// <param name="record">Record containing parameters to be substituded into the view.</param>
132 public void Execute(Record record)
133 {
134 var error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
135 if (0 != error)
136 {
137 throw new MsiException(error);
138 }
139 }
140
141 /// <summary>
142 /// Fetches the next row in the view.
143 /// </summary>
144 /// <returns>Returns the fetched record; otherwise null.</returns>
145 public Record Fetch()
146 {
147 var error = MsiInterop.MsiViewFetch(this.Handle, out var recordHandle);
148 if (259 == error)
149 {
150 return null;
151 }
152 else if (0 != error)
153 {
154 throw new MsiException(error);
155 }
156
157 return new Record(recordHandle);
158 }
159
160 /// <summary>
161 /// Updates a fetched record.
162 /// </summary>
163 /// <param name="type">Type of modification mode.</param>
164 /// <param name="record">Record to be modified.</param>
165 public void Modify(ModifyView type, Record record)
166 {
167 int error = MsiInterop.MsiViewModify(this.Handle, Convert.ToInt32(type, CultureInfo.InvariantCulture), record.Handle);
168 if (0 != error)
169 {
170 throw new MsiException(error);
171 }
172 }
173
174 /// <summary>
175 /// Returns a record containing column names or definitions.
176 /// </summary>
177 /// <param name="columnType">Specifies a flag indicating what type of information is needed. Either MSICOLINFO_NAMES or MSICOLINFO_TYPES.</param>
178 /// <returns>The record containing information about the column.</returns>
179 public Record GetColumnInfo(int columnType)
180 {
181 uint recordHandle;
182
183 int error = MsiInterop.MsiViewGetColumnInfo(this.Handle, columnType, out recordHandle);
184 if (0 != error)
185 {
186 throw new MsiException(error);
187 }
188
189 return new Record(recordHandle);
190 }
191
192 private class ViewEnumerable : IEnumerable<Record>
193 {
194 private readonly View view;
195
196 public ViewEnumerable(View view) => this.view = view;
197
198 public IEnumerator<Record> GetEnumerator() => new ViewEnumerator(this.view);
199
200 IEnumerator IEnumerable.GetEnumerator() => new ViewEnumerator(this.view);
201 }
202
203 private class ViewEnumerator : IEnumerator<Record>
204 {
205 private readonly View view;
206 private readonly List<Record> records = new List<Record>();
207 private int position = -1;
208 private bool disposed;
209
210 public ViewEnumerator(View view) => this.view = view;
211
212 public Record Current => this.records[this.position];
213
214 object IEnumerator.Current => this.records[this.position];
215
216 public bool MoveNext()
217 {
218 if (this.position + 1 >= this.records.Count)
219 {
220 var record = this.view.Fetch();
221
222 if (record == null)
223 {
224 return false;
225 }
226
227 this.records.Add(record);
228 this.position = this.records.Count - 1;
229 }
230 else
231 {
232 ++this.position;
233 }
234
235 return true;
236 }
237
238 public void Reset() => this.position = -1;
239
240 public void Dispose()
241 {
242 this.Dispose(true);
243 }
244
245 protected virtual void Dispose(bool disposing)
246 {
247 if (!this.disposed)
248 {
249 if (disposing)
250 {
251 foreach (var record in this.records)
252 {
253 record.Dispose();
254 }
255 }
256
257 this.disposed = true;
258 }
259 }
260 }
261 }
262}
diff --git a/src/WixToolset.Core.WindowsInstaller/Msi/WixInvalidIdtException.cs b/src/WixToolset.Core.WindowsInstaller/Msi/WixInvalidIdtException.cs
deleted file mode 100644
index a4750723..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Msi/WixInvalidIdtException.cs
+++ /dev/null
@@ -1,33 +0,0 @@
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.WindowsInstaller.Msi
4{
5 using System;
6 using WixToolset.Data;
7
8 /// <summary>
9 /// WiX invalid idt exception.
10 /// </summary>
11 [Serializable]
12 public sealed class WixInvalidIdtException : WixException
13 {
14 /// <summary>
15 /// Instantiate a new WixInvalidIdtException.
16 /// </summary>
17 /// <param name="idtFile">The invalid idt file.</param>
18 public WixInvalidIdtException(string idtFile) :
19 base(ErrorMessages.InvalidIdt(new SourceLineNumber(idtFile), idtFile))
20 {
21 }
22
23 /// <summary>
24 /// Instantiate a new WixInvalidIdtException.
25 /// </summary>
26 /// <param name="idtFile">The invalid idt file.</param>
27 /// <param name="tableName">The table name of the invalid idt file.</param>
28 public WixInvalidIdtException(string idtFile, string tableName) :
29 base(ErrorMessages.InvalidIdt(new SourceLineNumber(idtFile), idtFile, tableName))
30 {
31 }
32 }
33}
diff --git a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
index 1caa9e29..c46b6027 100644
--- a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
@@ -7,7 +7,7 @@ namespace WixToolset.Core.WindowsInstaller
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.WindowsInstaller.Bind; 9 using WixToolset.Core.WindowsInstaller.Bind;
10 using WixToolset.Core.WindowsInstaller.Msi; 10 using WixToolset.Core.Native.Msi;
11 using WixToolset.Core.WindowsInstaller.Unbind; 11 using WixToolset.Core.WindowsInstaller.Unbind;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.Symbols; 13 using WixToolset.Data.Symbols;
diff --git a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs b/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
deleted file mode 100644
index 541d899a..00000000
--- a/src/WixToolset.Core.WindowsInstaller/Ole32/Storage.cs
+++ /dev/null
@@ -1,437 +0,0 @@
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.Ole32
4{
5 using System;
6 using System.Runtime.InteropServices;
7 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
8 using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
9
10 /// <summary>
11 /// Specifies the access mode to use when opening, creating, or deleting a storage object.
12 /// </summary>
13 internal enum StorageMode
14 {
15 /// <summary>
16 /// Indicates that the object is read-only, meaning that modifications cannot be made.
17 /// </summary>
18 Read = 0x0,
19
20 /// <summary>
21 /// Enables you to save changes to the object, but does not permit access to its data.
22 /// </summary>
23 Write = 0x1,
24
25 /// <summary>
26 /// Enables access and modification of object data.
27 /// </summary>
28 ReadWrite = 0x2,
29
30 /// <summary>
31 /// Specifies that subsequent openings of the object are not denied read or write access.
32 /// </summary>
33 ShareDenyNone = 0x40,
34
35 /// <summary>
36 /// Prevents others from subsequently opening the object in Read mode.
37 /// </summary>
38 ShareDenyRead = 0x30,
39
40 /// <summary>
41 /// Prevents others from subsequently opening the object for Write or ReadWrite access.
42 /// </summary>
43 ShareDenyWrite = 0x20,
44
45 /// <summary>
46 /// Prevents others from subsequently opening the object in any mode.
47 /// </summary>
48 ShareExclusive = 0x10,
49
50 /// <summary>
51 /// Opens the storage object with exclusive access to the most recently committed version.
52 /// </summary>
53 Priority = 0x40000,
54
55 /// <summary>
56 /// Indicates that an existing storage object or stream should be removed before the new object replaces it.
57 /// </summary>
58 Create = 0x1000,
59 }
60
61 /// <summary>
62 /// Wrapper for the compound storage file APIs.
63 /// </summary>
64 internal sealed class Storage : IDisposable
65 {
66 private readonly IStorage storage;
67 private bool disposed;
68
69 /// <summary>
70 /// Instantiate a new Storage.
71 /// </summary>
72 /// <param name="storage">The native storage interface.</param>
73 private Storage(IStorage storage)
74 {
75 this.storage = storage;
76 }
77
78 /// <summary>
79 /// Storage destructor.
80 /// </summary>
81 ~Storage()
82 {
83 this.Dispose();
84 }
85
86 /// <summary>
87 /// The IEnumSTATSTG interface enumerates an array of STATSTG structures.
88 /// </summary>
89 [ComImport, Guid("0000000d-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
90 public interface IEnumSTATSTG
91 {
92 /// <summary>
93 /// Gets a specified number of STATSTG structures.
94 /// </summary>
95 /// <param name="celt">The number of STATSTG structures requested.</param>
96 /// <param name="rgelt">An array of STATSTG structures returned.</param>
97 /// <param name="pceltFetched">The number of STATSTG structures retrieved in the rgelt parameter.</param>
98 /// <returns>The error code.</returns>
99 [PreserveSig]
100 uint Next(uint celt, [MarshalAs(UnmanagedType.LPArray), Out] STATSTG[] rgelt, out uint pceltFetched);
101
102 /// <summary>
103 /// Skips a specified number of STATSTG structures in the enumeration sequence.
104 /// </summary>
105 /// <param name="celt">The number of STATSTG structures to skip.</param>
106 void Skip(uint celt);
107
108 /// <summary>
109 /// Resets the enumeration sequence to the beginning of the STATSTG structure array.
110 /// </summary>
111 void Reset();
112
113 /// <summary>
114 /// Creates a new enumerator that contains the same enumeration state as the current STATSTG structure enumerator.
115 /// </summary>
116 /// <returns>The cloned IEnumSTATSTG interface.</returns>
117 [return: MarshalAs(UnmanagedType.Interface)]
118 IEnumSTATSTG Clone();
119 }
120
121 /// <summary>
122 /// The IStorage interface supports the creation and management of structured storage objects.
123 /// </summary>
124 [ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
125 private interface IStorage
126 {
127 /// <summary>
128 /// Creates and opens a stream object with the specified name contained in this storage object.
129 /// </summary>
130 /// <param name="pwcsName">The name of the newly created stream.</param>
131 /// <param name="grfMode">Specifies the access mode to use when opening the newly created stream.</param>
132 /// <param name="reserved1">Reserved for future use; must be zero.</param>
133 /// <param name="reserved2">Reserved for future use; must be zero.</param>
134 /// <param name="ppstm">On return, pointer to the location of the new IStream interface pointer.</param>
135 void CreateStream(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStream ppstm);
136
137 /// <summary>
138 /// Opens an existing stream object within this storage object using the specified access permissions in grfMode.
139 /// </summary>
140 /// <param name="pwcsName">The name of the stream to open.</param>
141 /// <param name="reserved1">Reserved for future use; must be NULL.</param>
142 /// <param name="grfMode">Specifies the access mode to be assigned to the open stream.</param>
143 /// <param name="reserved2">Reserved for future use; must be zero.</param>
144 /// <param name="ppstm">A pointer to IStream pointer variable that receives the interface pointer to the newly opened stream object.</param>
145 void OpenStream(string pwcsName, IntPtr reserved1, uint grfMode, uint reserved2, out IStream ppstm);
146
147 /// <summary>
148 /// Creates and opens a new storage object nested within this storage object with the specified name in the specified access mode.
149 /// </summary>
150 /// <param name="pwcsName">The name of the newly created storage object.</param>
151 /// <param name="grfMode">A value that specifies the access mode to use when opening the newly created storage object.</param>
152 /// <param name="reserved1">Reserved for future use; must be zero.</param>
153 /// <param name="reserved2">Reserved for future use; must be zero.</param>
154 /// <param name="ppstg">A pointer, when successful, to the location of the IStorage pointer to the newly created storage object.</param>
155 void CreateStorage(string pwcsName, uint grfMode, uint reserved1, uint reserved2, out IStorage ppstg);
156
157 /// <summary>
158 /// Opens an existing storage object with the specified name in the specified access mode.
159 /// </summary>
160 /// <param name="pwcsName">The name of the storage object to open.</param>
161 /// <param name="pstgPriority">Must be NULL.</param>
162 /// <param name="grfMode">Specifies the access mode to use when opening the storage object.</param>
163 /// <param name="snbExclude">Must be NULL.</param>
164 /// <param name="reserved">Reserved for future use; must be zero.</param>
165 /// <param name="ppstg">When successful, pointer to the location of an IStorage pointer to the opened storage object.</param>
166 void OpenStorage(string pwcsName, IStorage pstgPriority, uint grfMode, IntPtr snbExclude, uint reserved, out IStorage ppstg);
167
168 /// <summary>
169 /// Copies the entire contents of an open storage object to another storage object.
170 /// </summary>
171 /// <param name="ciidExclude">The number of elements in the array pointed to by rgiidExclude.</param>
172 /// <param name="rgiidExclude">An array of interface identifiers (IIDs) that either the caller knows about and does not want
173 /// copied or that the storage object does not support, but whose state the caller will later explicitly copy.</param>
174 /// <param name="snbExclude">A string name block (refer to SNB) that specifies a block of storage or stream objects that are not to be copied to the destination.</param>
175 /// <param name="pstgDest">A pointer to the open storage object into which this storage object is to be copied.</param>
176 void CopyTo(uint ciidExclude, IntPtr rgiidExclude, IntPtr snbExclude, IStorage pstgDest);
177
178 /// <summary>
179 /// Copies or moves a substorage or stream from this storage object to another storage object.
180 /// </summary>
181 /// <param name="pwcsName">The name of the element in this storage object to be moved or copied.</param>
182 /// <param name="pstgDest">IStorage pointer to the destination storage object.</param>
183 /// <param name="pwcsNewName">The new name for the element in its new storage object.</param>
184 /// <param name="grfFlags">Specifies whether the operation should be a move (STGMOVE_MOVE) or a copy (STGMOVE_COPY).</param>
185 void MoveElementTo(string pwcsName, IStorage pstgDest, string pwcsNewName, uint grfFlags);
186
187 /// <summary>
188 /// Reflects changes for a transacted storage object to the parent level.
189 /// </summary>
190 /// <param name="grfCommitFlags">Controls how the changes are committed to the storage object.</param>
191 void Commit(uint grfCommitFlags);
192
193 /// <summary>
194 /// Discards all changes that have been made to the storage object since the last commit operation.
195 /// </summary>
196 void Revert();
197
198 /// <summary>
199 /// Returns an enumerator object that can be used to enumerate the storage and stream objects contained within this storage object.
200 /// </summary>
201 /// <param name="reserved1">Reserved for future use; must be zero.</param>
202 /// <param name="reserved2">Reserved for future use; must be NULL.</param>
203 /// <param name="reserved3">Reserved for future use; must be zero.</param>
204 /// <param name="ppenum">Pointer to IEnumSTATSTG* pointer variable that receives the interface pointer to the new enumerator object.</param>
205 void EnumElements(uint reserved1, IntPtr reserved2, uint reserved3, out IEnumSTATSTG ppenum);
206
207 /// <summary>
208 /// Removes the specified storage or stream from this storage object.
209 /// </summary>
210 /// <param name="pwcsName">The name of the storage or stream to be removed.</param>
211 void DestroyElement(string pwcsName);
212
213 /// <summary>
214 /// Renames the specified storage or stream in this storage object.
215 /// </summary>
216 /// <param name="pwcsOldName">The name of the substorage or stream to be changed.</param>
217 /// <param name="pwcsNewName">The new name for the specified substorage or stream.</param>
218 void RenameElement(string pwcsOldName, string pwcsNewName);
219
220 /// <summary>
221 /// Sets the modification, access, and creation times of the indicated storage element, if supported by the underlying file system.
222 /// </summary>
223 /// <param name="pwcsName">The name of the storage object element whose times are to be modified.</param>
224 /// <param name="pctime">Either the new creation time for the element or NULL if the creation time is not to be modified.</param>
225 /// <param name="patime">Either the new access time for the element or NULL if the access time is not to be modified.</param>
226 /// <param name="pmtime">Either the new modification time for the element or NULL if the modification time is not to be modified.</param>
227 void SetElementTimes(string pwcsName, FILETIME pctime, FILETIME patime, FILETIME pmtime);
228
229 /// <summary>
230 /// Assigns the specified CLSID to this storage object.
231 /// </summary>
232 /// <param name="clsid">The CLSID that is to be associated with the storage object.</param>
233 void SetClass(Guid clsid);
234
235 /// <summary>
236 /// Stores up to 32 bits of state information in this storage object.
237 /// </summary>
238 /// <param name="grfStateBits">Specifies the new values of the bits to set.</param>
239 /// <param name="grfMask">A binary mask indicating which bits in grfStateBits are significant in this call.</param>
240 void SetStateBits(uint grfStateBits, uint grfMask);
241
242 /// <summary>
243 /// Returns the STATSTG structure for this open storage object.
244 /// </summary>
245 /// <param name="pstatstg">On return, pointer to a STATSTG structure where this method places information about the open storage object.</param>
246 /// <param name="grfStatFlag">Specifies that some of the members in the STATSTG structure are not returned, thus saving a memory allocation operation.</param>
247 void Stat(out STATSTG pstatstg, uint grfStatFlag);
248 }
249
250 /// <summary>
251 /// The IStream interface lets you read and write data to stream objects.
252 /// </summary>
253 [ComImport, Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
254 private interface IStream
255 {
256 /// <summary>
257 /// Reads a specified number of bytes from the stream object into memory starting at the current seek pointer.
258 /// </summary>
259 /// <param name="pv">A pointer to the buffer which the stream data is read into.</param>
260 /// <param name="cb">The number of bytes of data to read from the stream object.</param>
261 /// <param name="pcbRead">A pointer to a ULONG variable that receives the actual number of bytes read from the stream object.</param>
262 void Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbRead);
263
264 /// <summary>
265 /// Writes a specified number of bytes into the stream object starting at the current seek pointer.
266 /// </summary>
267 /// <param name="pv">A pointer to the buffer that contains the data that is to be written to the stream.</param>
268 /// <param name="cb">The number of bytes of data to attempt to write into the stream.</param>
269 /// <param name="pcbWritten">A pointer to a ULONG variable where this method writes the actual number of bytes written to the stream object.</param>
270 void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbWritten);
271
272 /// <summary>
273 /// Changes the seek pointer to a new location relative to the beginning of the stream, the end of the stream, or the current seek pointer.
274 /// </summary>
275 /// <param name="dlibMove">The displacement to be added to the location indicated by the dwOrigin parameter.</param>
276 /// <param name="dwOrigin">The origin for the displacement specified in dlibMove.</param>
277 /// <param name="plibNewPosition">A pointer to the location where this method writes the value of the new seek pointer from the beginning of the stream.</param>
278 void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition);
279
280 /// <summary>
281 /// Changes the size of the stream object.
282 /// </summary>
283 /// <param name="libNewSize">Specifies the new size of the stream as a number of bytes.</param>
284 void SetSize(long libNewSize);
285
286 /// <summary>
287 /// Copies a specified number of bytes from the current seek pointer in the stream to the current seek pointer in another stream.
288 /// </summary>
289 /// <param name="pstm">A pointer to the destination stream.</param>
290 /// <param name="cb">The number of bytes to copy from the source stream.</param>
291 /// <param name="pcbRead">A pointer to the location where this method writes the actual number of bytes read from the source.</param>
292 /// <param name="pcbWritten">A pointer to the location where this method writes the actual number of bytes written to the destination.</param>
293 void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);
294
295 /// <summary>
296 /// Ensures that any changes made to a stream object open in transacted mode are reflected in the parent storage object.
297 /// </summary>
298 /// <param name="grfCommitFlags">Controls how the changes for the stream object are committed.</param>
299 void Commit(int grfCommitFlags);
300
301 /// <summary>
302 /// Discards all changes that have been made to a transacted stream since the last call to IStream::Commit.
303 /// </summary>
304 void Revert();
305
306 /// <summary>
307 /// Restricts access to a specified range of bytes in the stream.
308 /// </summary>
309 /// <param name="libOffset">Integer that specifies the byte offset for the beginning of the range.</param>
310 /// <param name="cb">Integer that specifies the length of the range, in bytes, to be restricted.</param>
311 /// <param name="dwLockType">Specifies the restrictions being requested on accessing the range.</param>
312 void LockRegion(long libOffset, long cb, int dwLockType);
313
314 /// <summary>
315 /// Removes the access restriction on a range of bytes previously restricted with IStream::LockRegion.
316 /// </summary>
317 /// <param name="libOffset">Specifies the byte offset for the beginning of the range.</param>
318 /// <param name="cb">Specifies, in bytes, the length of the range to be restricted.</param>
319 /// <param name="dwLockType">Specifies the access restrictions previously placed on the range.</param>
320 void UnlockRegion(long libOffset, long cb, int dwLockType);
321
322 /// <summary>
323 /// Retrieves the STATSTG structure for this stream.
324 /// </summary>
325 /// <param name="pstatstg">Pointer to a STATSTG structure where this method places information about this stream object.</param>
326 /// <param name="grfStatFlag">Specifies that this method does not return some of the members in the STATSTG structure, thus saving a memory allocation operation.</param>
327 void Stat(out STATSTG pstatstg, int grfStatFlag);
328
329 /// <summary>
330 /// Creates a new stream object that references the same bytes as the original stream but provides a separate seek pointer to those bytes.
331 /// </summary>
332 /// <param name="ppstm">When successful, pointer to the location of an IStream pointer to the new stream object.</param>
333 void Clone(out IStream ppstm);
334 }
335
336 /// <summary>
337 /// Creates a new compound file storage object.
338 /// </summary>
339 /// <param name="storageFile">The compound file being created.</param>
340 /// <param name="mode">Specifies the access mode to use when opening the new storage object.</param>
341 /// <returns>The created Storage object.</returns>
342 public static Storage CreateDocFile(string storageFile, StorageMode mode)
343 {
344 IStorage storage = NativeMethods.StgCreateDocfile(storageFile, (uint)mode, 0);
345
346 return new Storage(storage);
347 }
348
349 /// <summary>
350 /// Opens an existing root storage object in the file system.
351 /// </summary>
352 /// <param name="storageFile">The file that contains the storage object to open.</param>
353 /// <param name="mode">Specifies the access mode to use to open the storage object.</param>
354 /// <returns>The created Storage object.</returns>
355 public static Storage Open(string storageFile, StorageMode mode)
356 {
357 IStorage storage = NativeMethods.StgOpenStorage(storageFile, IntPtr.Zero, (uint)mode, IntPtr.Zero, 0);
358
359 return new Storage(storage);
360 }
361
362 /// <summary>
363 /// Copies the entire contents of this open storage object into another Storage object.
364 /// </summary>
365 /// <param name="destinationStorage">The destination Storage object.</param>
366 public void CopyTo(Storage destinationStorage)
367 {
368 this.storage.CopyTo(0, IntPtr.Zero, IntPtr.Zero, destinationStorage.storage);
369 }
370
371 /// <summary>
372 /// Opens an existing Storage object with the specified name according to the specified access mode.
373 /// </summary>
374 /// <param name="name">The name of the Storage object.</param>
375 /// <returns>The opened Storage object.</returns>
376 public Storage OpenStorage(string name)
377 {
378 IStorage subStorage;
379
380 this.storage.OpenStorage(name, null, (uint)(StorageMode.Read | StorageMode.ShareExclusive), IntPtr.Zero, 0, out subStorage);
381
382 return new Storage(subStorage);
383 }
384
385 /// <summary>
386 /// Disposes the managed and unmanaged objects in this object.
387 /// </summary>
388 public void Dispose()
389 {
390 if (!this.disposed)
391 {
392 Marshal.ReleaseComObject(this.storage);
393
394 this.disposed = true;
395 }
396
397 GC.SuppressFinalize(this);
398 }
399
400 /// <summary>
401 /// The native methods.
402 /// </summary>
403 private sealed class NativeMethods
404 {
405 /// <summary>
406 /// Protect the constructor since this class only contains static methods.
407 /// </summary>
408 private NativeMethods()
409 {
410 }
411
412 /// <summary>
413 /// Creates a new compound file storage object.
414 /// </summary>
415 /// <param name="pwcsName">The name for the compound file being created.</param>
416 /// <param name="grfMode">Specifies the access mode to use when opening the new storage object.</param>
417 /// <param name="reserved">Reserved for future use; must be zero.</param>
418 /// <returns>A pointer to the location of the IStorage pointer to the new storage object.</returns>
419 [DllImport("ole32.dll", PreserveSig = false)]
420 [return: MarshalAs(UnmanagedType.Interface)]
421 internal static extern IStorage StgCreateDocfile([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, uint grfMode, uint reserved);
422
423 /// <summary>
424 /// Opens an existing root storage object in the file system.
425 /// </summary>
426 /// <param name="pwcsName">The file that contains the storage object to open.</param>
427 /// <param name="pstgPriority">Most often NULL.</param>
428 /// <param name="grfMode">Specifies the access mode to use to open the storage object.</param>
429 /// <param name="snbExclude">If not NULL, pointer to a block of elements in the storage to be excluded as the storage object is opened.</param>
430 /// <param name="reserved">Indicates reserved for future use; must be zero.</param>
431 /// <returns>A pointer to a IStorage* pointer variable that receives the interface pointer to the opened storage.</returns>
432 [DllImport("ole32.dll", PreserveSig = false)]
433 [return: MarshalAs(UnmanagedType.Interface)]
434 internal static extern IStorage StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr pstgPriority, uint grfMode, IntPtr snbExclude, uint reserved);
435 }
436 }
437}
diff --git a/src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs b/src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs
deleted file mode 100644
index 3874d8e7..00000000
--- a/src/WixToolset.Core.WindowsInstaller/PatchAPI/PatchInterop.cs
+++ /dev/null
@@ -1,989 +0,0 @@
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.PatchAPI
4{
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics.CodeAnalysis;
8 using System.Globalization;
9 using System.Runtime.InteropServices;
10 using WixToolset.Data.Symbols;
11
12 /// <summary>
13 /// Interop class for the mspatchc.dll.
14 /// </summary>
15 internal static class PatchInterop
16 {
17 // From WinError.h in the Platform SDK
18 internal const ushort FACILITY_WIN32 = 7;
19
20 /// <summary>
21 /// Parse a number from text in either hex or decimal.
22 /// </summary>
23 /// <param name="source">Source value. Treated as hex if it starts 0x (or 0X), decimal otherwise.</param>
24 /// <returns>Numeric value that source represents.</returns>
25 static internal UInt32 ParseHexOrDecimal(string source)
26 {
27 string value = source.Trim();
28 if (String.Equals(value.Substring(0, 2), "0x", StringComparison.OrdinalIgnoreCase))
29 {
30 return UInt32.Parse(value.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat);
31 }
32 else
33 {
34 return UInt32.Parse(value, CultureInfo.InvariantCulture.NumberFormat);
35 }
36 }
37
38 /// <summary>
39 /// Create a binary delta file.
40 /// </summary>
41 /// <param name="deltaFile">Name of the delta file to create.</param>
42 /// <param name="targetFile">Name of updated file.</param>
43 /// <param name="targetSymbolPath">Optional paths to updated file's symbols.</param>
44 /// <param name="targetRetainOffsets">Optional offsets to the delta retain sections in the updated file.</param>
45 /// <param name="basisFiles">Optional array of target files.</param>
46 /// <param name="basisSymbolPaths">Optional array of target files' symbol paths (must match basisFiles array).</param>
47 /// <param name="basisIgnoreLengths">Optional array of target files' delta ignore section lengths (must match basisFiles array)(each entry must match basisIgnoreOffsets entries).</param>
48 /// <param name="basisIgnoreOffsets">Optional array of target files' delta ignore section offsets (must match basisFiles array)(each entry must match basisIgnoreLengths entries).</param>
49 /// <param name="basisRetainLengths">Optional array of target files' delta protect section lengths (must match basisFiles array)(each entry must match basisRetainOffsets and targetRetainOffsets entries).</param>
50 /// <param name="basisRetainOffsets">Optional array of target files' delta protect section offsets (must match basisFiles array)(each entry must match basisRetainLengths and targetRetainOffsets entries).</param>
51 /// <param name="apiPatchingSymbolFlags">ApiPatchingSymbolFlags value.</param>
52 /// <param name="optimizePatchSizeForLargeFiles">OptimizePatchSizeForLargeFiles value.</param>
53 /// <param name="retainRangesIgnored">Flag to indicate retain ranges were ignored due to mismatch.</param>
54 /// <returns>true if delta file was created, false if whole file should be used instead.</returns>
55 static public bool CreateDelta(
56 string deltaFile,
57 string targetFile,
58 string targetSymbolPath,
59 string targetRetainOffsets,
60 string[] basisFiles,
61 string[] basisSymbolPaths,
62 string[] basisIgnoreLengths,
63 string[] basisIgnoreOffsets,
64 string[] basisRetainLengths,
65 string[] basisRetainOffsets,
66 PatchSymbolFlags apiPatchingSymbolFlags,
67 bool optimizePatchSizeForLargeFiles,
68 out bool retainRangesIgnored
69 )
70 {
71 retainRangesIgnored = false;
72 if (0 != (apiPatchingSymbolFlags & ~(PatchSymbolFlags.PatchSymbolNoImagehlp | PatchSymbolFlags.PatchSymbolNoFailures | PatchSymbolFlags.PatchSymbolUndecoratedToo)))
73 {
74 throw new ArgumentOutOfRangeException("apiPatchingSymbolFlags");
75 }
76
77 if (null == deltaFile || 0 == deltaFile.Length)
78 {
79 throw new ArgumentNullException("deltaFile");
80 }
81
82 if (null == targetFile || 0 == targetFile.Length)
83 {
84 throw new ArgumentNullException("targetFile");
85 }
86
87 if (null == basisFiles || 0 == basisFiles.Length)
88 {
89 return false;
90 }
91 uint countOldFiles = (uint)basisFiles.Length;
92
93 if (null != basisSymbolPaths)
94 {
95 if (0 != basisSymbolPaths.Length)
96 {
97 if ((uint)basisSymbolPaths.Length != countOldFiles)
98 {
99 throw new ArgumentOutOfRangeException("basisSymbolPaths");
100 }
101 }
102 }
103 // a null basisSymbolPaths is allowed.
104
105 if (null != basisIgnoreLengths)
106 {
107 if (0 != basisIgnoreLengths.Length)
108 {
109 if ((uint)basisIgnoreLengths.Length != countOldFiles)
110 {
111 throw new ArgumentOutOfRangeException("basisIgnoreLengths");
112 }
113 }
114 }
115 else
116 {
117 basisIgnoreLengths = new string[countOldFiles];
118 }
119
120 if (null != basisIgnoreOffsets)
121 {
122 if (0 != basisIgnoreOffsets.Length)
123 {
124 if ((uint)basisIgnoreOffsets.Length != countOldFiles)
125 {
126 throw new ArgumentOutOfRangeException("basisIgnoreOffsets");
127 }
128 }
129 }
130 else
131 {
132 basisIgnoreOffsets = new string[countOldFiles];
133 }
134
135 if (null != basisRetainLengths)
136 {
137 if (0 != basisRetainLengths.Length)
138 {
139 if ((uint)basisRetainLengths.Length != countOldFiles)
140 {
141 throw new ArgumentOutOfRangeException("basisRetainLengths");
142 }
143 }
144 }
145 else
146 {
147 basisRetainLengths = new string[countOldFiles];
148 }
149
150 if (null != basisRetainOffsets)
151 {
152 if (0 != basisRetainOffsets.Length)
153 {
154 if ((uint)basisRetainOffsets.Length != countOldFiles)
155 {
156 throw new ArgumentOutOfRangeException("basisRetainOffsets");
157 }
158 }
159 }
160 else
161 {
162 basisRetainOffsets = new string[countOldFiles];
163 }
164
165 PatchOptionData pod = new PatchOptionData();
166 pod.symbolOptionFlags = apiPatchingSymbolFlags;
167 pod.newFileSymbolPath = targetSymbolPath;
168 pod.oldFileSymbolPathArray = basisSymbolPaths;
169 pod.extendedOptionFlags = 0;
170 PatchOldFileInfoW[] oldFileInfoArray = new PatchOldFileInfoW[countOldFiles];
171 string[] newRetainOffsetArray = ((null == targetRetainOffsets) ? new string[0] : targetRetainOffsets.Split(','));
172 for (uint i = 0; i < countOldFiles; ++i)
173 {
174 PatchOldFileInfoW ofi = new PatchOldFileInfoW();
175 ofi.oldFileName = basisFiles[i];
176 string[] ignoreLengthArray = ((null == basisIgnoreLengths[i]) ? new string[0] : basisIgnoreLengths[i].Split(','));
177 string[] ignoreOffsetArray = ((null == basisIgnoreOffsets[i]) ? new string[0] : basisIgnoreOffsets[i].Split(','));
178 string[] retainLengthArray = ((null == basisRetainLengths[i]) ? new string[0] : basisRetainLengths[i].Split(','));
179 string[] retainOffsetArray = ((null == basisRetainOffsets[i]) ? new string[0] : basisRetainOffsets[i].Split(','));
180 // Validate inputs
181 if (ignoreLengthArray.Length != ignoreOffsetArray.Length)
182 {
183 throw new ArgumentOutOfRangeException("basisIgnoreLengths");
184 }
185
186 if (retainLengthArray.Length != retainOffsetArray.Length)
187 {
188 throw new ArgumentOutOfRangeException("basisRetainLengths");
189 }
190
191 if (newRetainOffsetArray.Length != retainOffsetArray.Length)
192 {
193 // remove all retain range information
194 retainRangesIgnored = true;
195 for (uint j = 0; j < countOldFiles; ++j)
196 {
197 basisRetainLengths[j] = null;
198 basisRetainOffsets[j] = null;
199 }
200 retainLengthArray = new string[0];
201 retainOffsetArray = new string[0];
202 newRetainOffsetArray = new string[0];
203 for (uint j = 0; j < oldFileInfoArray.Length; ++j)
204 {
205 oldFileInfoArray[j].retainRange = null;
206 }
207 }
208
209 // Populate IgnoreRange structure
210 PatchIgnoreRange[] ignoreArray = null;
211 if (0 != ignoreLengthArray.Length)
212 {
213 ignoreArray = new PatchIgnoreRange[ignoreLengthArray.Length];
214 for (int j = 0; j < ignoreLengthArray.Length; ++j)
215 {
216 PatchIgnoreRange ignoreRange = new PatchIgnoreRange();
217 ignoreRange.offsetInOldFile = ParseHexOrDecimal(ignoreOffsetArray[j]);
218 ignoreRange.lengthInBytes = ParseHexOrDecimal(ignoreLengthArray[j]);
219 ignoreArray[j] = ignoreRange;
220 }
221 ofi.ignoreRange = ignoreArray;
222 }
223
224 PatchRetainRange[] retainArray = null;
225 if (0 != newRetainOffsetArray.Length)
226 {
227 retainArray = new PatchRetainRange[retainLengthArray.Length];
228 for (int j = 0; j < newRetainOffsetArray.Length; ++j)
229 {
230 PatchRetainRange retainRange = new PatchRetainRange();
231 retainRange.offsetInOldFile = ParseHexOrDecimal(retainOffsetArray[j]);
232 retainRange.lengthInBytes = ParseHexOrDecimal(retainLengthArray[j]);
233 retainRange.offsetInNewFile = ParseHexOrDecimal(newRetainOffsetArray[j]);
234 retainArray[j] = retainRange;
235 }
236 ofi.retainRange = retainArray;
237 }
238 oldFileInfoArray[i] = ofi;
239 }
240
241 if (CreatePatchFileExW(
242 countOldFiles,
243 oldFileInfoArray,
244 targetFile,
245 deltaFile,
246 PatchOptionFlags(optimizePatchSizeForLargeFiles),
247 pod,
248 null,
249 IntPtr.Zero))
250 {
251 return true;
252 }
253
254 // determine if this is an error or a need to use whole file.
255 int err = Marshal.GetLastWin32Error();
256 switch (err)
257 {
258 case unchecked((int)ERROR_PATCH_BIGGER_THAN_COMPRESSED):
259 break;
260
261 // too late to exclude this file -- should have been caught before
262 case unchecked((int)ERROR_PATCH_SAME_FILE):
263 default:
264 throw new System.ComponentModel.Win32Exception(err);
265 }
266 return false;
267 }
268
269 /// <summary>
270 /// Extract the delta header.
271 /// </summary>
272 /// <param name="delta">Name of delta file.</param>
273 /// <param name="deltaHeader">Name of file to create with the delta's header.</param>
274 static public void ExtractDeltaHeader(string delta, string deltaHeader)
275 {
276 if (!ExtractPatchHeaderToFileW(delta, deltaHeader))
277 {
278 throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
279 }
280 }
281
282 /// <summary>
283 /// Returns the PatchOptionFlags to use.
284 /// </summary>
285 /// <param name="optimizeForLargeFiles">True if optimizing for large files.</param>
286 /// <returns>PATCH_OPTION_FLAG values</returns>
287 static private UInt32 PatchOptionFlags(bool optimizeForLargeFiles)
288 {
289 UInt32 flags = PATCH_OPTION_FAIL_IF_SAME_FILE | PATCH_OPTION_FAIL_IF_BIGGER | PATCH_OPTION_USE_LZX_BEST;
290 if (optimizeForLargeFiles)
291 {
292 flags |= PATCH_OPTION_USE_LZX_LARGE;
293 }
294 return flags;
295 }
296
297 //---------------------------------------------------------------------
298 // From PatchApi.h
299 //---------------------------------------------------------------------
300
301 //
302 // The following contants can be combined and used as the OptionFlags
303 // parameter in the patch creation apis.
304
305 internal const uint PATCH_OPTION_USE_BEST = 0x00000000; // auto choose best (slower)
306
307 internal const uint PATCH_OPTION_USE_LZX_BEST = 0x00000003; // auto choose best of LXZ A/B (but not large)
308 internal const uint PATCH_OPTION_USE_LZX_A = 0x00000001; // normal
309 internal const uint PATCH_OPTION_USE_LXZ_B = 0x00000002; // better on some x86 binaries
310 internal const uint PATCH_OPTION_USE_LZX_LARGE = 0x00000004; // better support for large files (requires 5.1 or higher applyer)
311
312 internal const uint PATCH_OPTION_NO_BINDFIX = 0x00010000; // PE bound imports
313 internal const uint PATCH_OPTION_NO_LOCKFIX = 0x00020000; // PE smashed locks
314 internal const uint PATCH_OPTION_NO_REBASE = 0x00040000; // PE rebased image
315 internal const uint PATCH_OPTION_FAIL_IF_SAME_FILE = 0x00080000; // don't create if same
316 internal const uint PATCH_OPTION_FAIL_IF_BIGGER = 0x00100000; // fail if patch is larger than simply compressing new file (slower)
317 internal const uint PATCH_OPTION_NO_CHECKSUM = 0x00200000; // PE checksum zero
318 internal const uint PATCH_OPTION_NO_RESTIMEFIX = 0x00400000; // PE resource timestamps
319 internal const uint PATCH_OPTION_NO_TIMESTAMP = 0x00800000; // don't store new file timestamp in patch
320 internal const uint PATCH_OPTION_SIGNATURE_MD5 = 0x01000000; // use MD5 instead of CRC (reserved for future support)
321 internal const uint PATCH_OPTION_INTERLEAVE_FILES = 0x40000000; // better support for large files (requires 5.2 or higher applyer)
322 internal const uint PATCH_OPTION_RESERVED1 = 0x80000000; // (used internally)
323
324 internal const uint PATCH_OPTION_VALID_FLAGS = 0xC0FF0007;
325
326 //
327 // The following flags are used with PATCH_OPTION_DATA ExtendedOptionFlags:
328 //
329
330 internal const uint PATCH_TRANSFORM_PE_RESOURCE_2 = 0x00000100; // better handling of PE resources (requires 5.2 or higher applyer)
331 internal const uint PATCH_TRANSFORM_PE_IRELOC_2 = 0x00000200; // better handling of PE stripped relocs (requires 5.2 or higher applyer)
332
333 //
334 // In addition to the standard Win32 error codes, the following error codes may
335 // be returned via GetLastError() when one of the patch APIs fails.
336
337 internal const uint ERROR_PATCH_ENCODE_FAILURE = 0xC00E3101; // create
338 internal const uint ERROR_PATCH_INVALID_OPTIONS = 0xC00E3102; // create
339 internal const uint ERROR_PATCH_SAME_FILE = 0xC00E3103; // create
340 internal const uint ERROR_PATCH_RETAIN_RANGES_DIFFER = 0xC00E3104; // create
341 internal const uint ERROR_PATCH_BIGGER_THAN_COMPRESSED = 0xC00E3105; // create
342 internal const uint ERROR_PATCH_IMAGEHLP_FALURE = 0xC00E3106; // create
343
344 /// <summary>
345 /// Delegate type that the PatchAPI calls for progress notification.
346 /// </summary>
347 /// <param name="context">.</param>
348 /// <param name="currentPosition">.</param>
349 /// <param name="maxPosition">.</param>
350 /// <returns>True for success</returns>
351 public delegate bool PatchProgressCallback(
352 IntPtr context,
353 uint currentPosition,
354 uint maxPosition
355 );
356
357 /// <summary>
358 /// Delegate type that the PatchAPI calls for patch symbol load information.
359 /// </summary>
360 /// <param name="whichFile">.</param>
361 /// <param name="symbolFileName">.</param>
362 /// <param name="symType">.</param>
363 /// <param name="symbolFileCheckSum">.</param>
364 /// <param name="symbolFileTimeDate">.</param>
365 /// <param name="imageFileCheckSum">.</param>
366 /// <param name="imageFileTimeDate">.</param>
367 /// <param name="context">.</param>
368 /// <returns>???</returns>
369 public delegate bool PatchSymloadCallback(
370 uint whichFile, // 0 for new file, 1 for first old file, etc
371 [MarshalAs(UnmanagedType.LPStr)] string symbolFileName,
372 uint symType, // see SYM_TYPE in imagehlp.h
373 uint symbolFileCheckSum,
374 uint symbolFileTimeDate,
375 uint imageFileCheckSum,
376 uint imageFileTimeDate,
377 IntPtr context
378 );
379
380 /// <summary>
381 /// Wraps PATCH_IGNORE_RANGE
382 /// </summary>
383 [StructLayout(LayoutKind.Sequential)]
384 internal class PatchIgnoreRange
385 {
386 public uint offsetInOldFile;
387 public uint lengthInBytes;
388 }
389
390 /// <summary>
391 /// Wraps PATCH_RETAIN_RANGE
392 /// </summary>
393 [StructLayout(LayoutKind.Sequential)]
394 internal class PatchRetainRange
395 {
396 public uint offsetInOldFile;
397 public uint lengthInBytes;
398 public uint offsetInNewFile;
399 }
400
401 /// <summary>
402 /// Wraps PATCH_OLD_FILE_INFO (except for the OldFile~ portion)
403 /// </summary>
404 internal class PatchOldFileInfo
405 {
406 public PatchIgnoreRange[] ignoreRange;
407 public PatchRetainRange[] retainRange;
408 }
409
410 /// <summary>
411 /// Wraps PATCH_OLD_FILE_INFO_W
412 /// </summary>
413 internal class PatchOldFileInfoW : PatchOldFileInfo
414 {
415 public string oldFileName;
416 }
417
418 /// <summary>
419 /// Wraps each PATCH_INTERLEAVE_MAP Range
420 /// </summary>
421 [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses"), StructLayout(LayoutKind.Sequential)]
422 internal class PatchInterleaveMapRange
423 {
424 public uint oldOffset;
425 public uint oldLength;
426 public uint newLength;
427 }
428
429 /// <summary>
430 /// Wraps PATCH_INTERLEAVE_MAP
431 /// </summary>
432 internal class PatchInterleaveMap
433 {
434 public PatchInterleaveMapRange[] ranges = null;
435 }
436
437
438 /// <summary>
439 /// Wraps PATCH_OPTION_DATA
440 /// </summary>
441 [BestFitMapping(false, ThrowOnUnmappableChar = true)]
442 internal class PatchOptionData
443 {
444 public PatchSymbolFlags symbolOptionFlags; // PATCH_SYMBOL_xxx flags
445 [MarshalAs(UnmanagedType.LPStr)] public string newFileSymbolPath; // always ANSI, never Unicode
446 [MarshalAs(UnmanagedType.LPStr)] public string[] oldFileSymbolPathArray; // array[ OldFileCount ]
447 public uint extendedOptionFlags;
448 public PatchSymloadCallback symLoadCallback = null;
449 public IntPtr symLoadContext = IntPtr.Zero;
450 public PatchInterleaveMap[] interleaveMapArray = null; // array[ OldFileCount ] (requires 5.2 or higher applyer)
451 public uint maxLzxWindowSize = 0; // limit memory requirements (requires 5.2 or higher applyer)
452 }
453
454 //
455 // Note that PATCH_OPTION_DATA contains LPCSTR paths, and no LPCWSTR (Unicode)
456 // path argument is available, even when used with one of the Unicode APIs
457 // such as CreatePatchFileW. This is because the unlerlying system services
458 // for symbol file handling (IMAGEHLP.DLL) only support ANSI file/path names.
459 //
460
461 //
462 // A note about PATCH_RETAIN_RANGE specifiers with multiple old files:
463 //
464 // Each old version file must have the same RetainRangeCount, and the same
465 // retain range LengthInBytes and OffsetInNewFile values in the same order.
466 // Only the OffsetInOldFile values can differ between old foles for retain
467 // ranges.
468 //
469
470 //
471 // The following prototypes are (some of the) interfaces for creating patches from files.
472 //
473
474 /// <summary>
475 /// Creates a new delta.
476 /// </summary>
477 /// <param name="oldFileCount">Size of oldFileInfoArray.</param>
478 /// <param name="oldFileInfoArray">Target file information.</param>
479 /// <param name="newFileName">Name of updated file.</param>
480 /// <param name="patchFileName">Name of delta to create.</param>
481 /// <param name="optionFlags">PATCH_OPTION_xxx.</param>
482 /// <param name="optionData">Optional PATCH_OPTION_DATA structure.</param>
483 /// <param name="progressCallback">Delegate for progress callbacks.</param>
484 /// <param name="context">Context for progress callback delegate.</param>
485 /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
486 [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
487 [return: MarshalAs(UnmanagedType.Bool)]
488 internal static extern bool CreatePatchFileExW(
489 uint oldFileCount, // maximum 255
490 [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OLD_FILE_INFO_W")]
491 PatchOldFileInfoW[] oldFileInfoArray,
492 string newFileName, // input file (required)
493 string patchFileName, // output file (required)
494 uint optionFlags,
495 [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(PatchAPIMarshaler), MarshalCookie="PATCH_OPTION_DATA")]
496 PatchOptionData optionData,
497 [MarshalAs (UnmanagedType.FunctionPtr)]
498 PatchProgressCallback progressCallback,
499 IntPtr context
500 );
501
502 /// <summary>
503 /// Extracts delta header from delta.
504 /// </summary>
505 /// <param name="patchFileName">Name of delta file.</param>
506 /// <param name="patchHeaderFileName">Name of file to create with delta header.</param>
507 /// <returns>true if successfull, sets Marshal.GetLastWin32Error() if not.</returns>
508 [DllImport("mspatchc.dll", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true)]
509 [return: MarshalAs(UnmanagedType.Bool)]
510 internal static extern bool ExtractPatchHeaderToFileW(
511 string patchFileName, // input file
512 string patchHeaderFileName // output file
513 );
514
515 // TODO: Add rest of APIs to enable custom binders to perform more exhaustive checks
516
517 /// <summary>
518 /// Marshals arguments for the CreatePatch~ APIs
519 /// </summary>
520 [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")]
521 internal class PatchAPIMarshaler : ICustomMarshaler
522 {
523 internal static ICustomMarshaler GetInstance(string cookie)
524 {
525 return new PatchAPIMarshaler(cookie);
526 }
527
528 private enum MarshalType
529 {
530 PATCH_OPTION_DATA,
531 PATCH_OLD_FILE_INFO_W
532 };
533 private PatchAPIMarshaler.MarshalType marshalType;
534
535 private PatchAPIMarshaler(string cookie)
536 {
537 this.marshalType = (PatchAPIMarshaler.MarshalType)Enum.Parse(typeof(PatchAPIMarshaler.MarshalType), cookie);
538 }
539
540 //
541 // Summary:
542 // Returns the size of the native data to be marshaled.
543 //
544 // Returns:
545 // The size in bytes of the native data.
546 public int GetNativeDataSize()
547 {
548 return Marshal.SizeOf(typeof(IntPtr));
549 }
550
551 //
552 // Summary:
553 // Performs necessary cleanup of the managed data when it is no longer needed.
554 //
555 // Parameters:
556 // ManagedObj:
557 // The managed object to be destroyed.
558 public void CleanUpManagedData(object ManagedObj)
559 {
560 }
561
562 //
563 // Summary:
564 // Performs necessary cleanup of the unmanaged data when it is no longer needed.
565 //
566 // Parameters:
567 // pNativeData:
568 // A pointer to the unmanaged data to be destroyed.
569 public void CleanUpNativeData(IntPtr pNativeData)
570 {
571 if (IntPtr.Zero == pNativeData)
572 {
573 return;
574 }
575
576 switch (this.marshalType)
577 {
578 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
579 this.CleanUpPOD(pNativeData);
580 break;
581 default:
582 this.CleanUpPOFI_A(pNativeData);
583 break;
584 }
585 }
586
587 //
588 // Summary:
589 // Converts the managed data to unmanaged data.
590 //
591 // Parameters:
592 // ManagedObj:
593 // The managed object to be converted.
594 //
595 // Returns:
596 // Returns the COM view of the managed object.
597 public IntPtr MarshalManagedToNative(object ManagedObj)
598 {
599 if (null == ManagedObj)
600 {
601 return IntPtr.Zero;
602 }
603
604 switch (this.marshalType)
605 {
606 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
607 return this.MarshalPOD(ManagedObj as PatchOptionData);
608 case PatchAPIMarshaler.MarshalType.PATCH_OLD_FILE_INFO_W:
609 return this.MarshalPOFIW_A(ManagedObj as PatchOldFileInfoW[]);
610 default:
611 throw new InvalidOperationException();
612 }
613 }
614
615
616 //
617 // Summary:
618 // Converts the unmanaged data to managed data.
619 //
620 // Parameters:
621 // pNativeData:
622 // A pointer to the unmanaged data to be wrapped.
623 //
624 // Returns:
625 // Returns the managed view of the COM data.
626 public object MarshalNativeToManaged(IntPtr pNativeData)
627 {
628 return null;
629 }
630
631 // Implementation *************************************************
632
633 // PATCH_OPTION_DATA offsets
634 private static readonly int symbolOptionFlagsOffset = Marshal.SizeOf(typeof(Int32));
635 private static readonly int newFileSymbolPathOffset = 2 * Marshal.SizeOf(typeof(Int32));
636 private static readonly int oldFileSymbolPathArrayOffset = 2 * Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
637 private static readonly int extendedOptionFlagsOffset = 2 * Marshal.SizeOf(typeof(Int32)) + 2 * Marshal.SizeOf(typeof(IntPtr));
638 private static readonly int symLoadCallbackOffset = 3 * Marshal.SizeOf(typeof(Int32)) + 2 * Marshal.SizeOf(typeof(IntPtr));
639 private static readonly int symLoadContextOffset = 3 * Marshal.SizeOf(typeof(Int32)) + 3 * Marshal.SizeOf(typeof(IntPtr));
640 private static readonly int interleaveMapArrayOffset = 3 * Marshal.SizeOf(typeof(Int32)) + 4 * Marshal.SizeOf(typeof(IntPtr));
641 private static readonly int maxLzxWindowSizeOffset = 3 * Marshal.SizeOf(typeof(Int32)) + 5 * Marshal.SizeOf(typeof(IntPtr));
642 private static readonly int patchOptionDataSize = 4 * Marshal.SizeOf(typeof(Int32)) + 5 * Marshal.SizeOf(typeof(IntPtr));
643
644 // PATCH_OLD_FILE_INFO offsets
645 private static readonly int oldFileOffset = Marshal.SizeOf(typeof(Int32));
646 private static readonly int ignoreRangeCountOffset = Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
647 private static readonly int ignoreRangeArrayOffset = 2 * Marshal.SizeOf(typeof(Int32)) + Marshal.SizeOf(typeof(IntPtr));
648 private static readonly int retainRangeCountOffset = 2 * Marshal.SizeOf(typeof(Int32)) + 2 * Marshal.SizeOf(typeof(IntPtr));
649 private static readonly int retainRangeArrayOffset = 3 * Marshal.SizeOf(typeof(Int32)) + 2 * Marshal.SizeOf(typeof(IntPtr));
650 private static readonly int patchOldFileInfoSize = 3 * Marshal.SizeOf(typeof(Int32)) + 3 * Marshal.SizeOf(typeof(IntPtr));
651
652 // Methods and data used to preserve data needed for cleanup
653
654 // This dictionary holds the quantity of items internal to each native structure that will need to be freed (the OldFileCount)
655 private static readonly Dictionary<IntPtr, int> OldFileCounts = new Dictionary<IntPtr, int>();
656 private static readonly object OldFileCountsLock = new object();
657
658 private IntPtr CreateMainStruct(int oldFileCount)
659 {
660 int nativeSize;
661 switch (this.marshalType)
662 {
663 case PatchAPIMarshaler.MarshalType.PATCH_OPTION_DATA:
664 nativeSize = patchOptionDataSize;
665 break;
666 case PatchAPIMarshaler.MarshalType.PATCH_OLD_FILE_INFO_W:
667 nativeSize = oldFileCount * patchOldFileInfoSize;
668 break;
669 default:
670 throw new InvalidOperationException();
671 }
672
673 IntPtr native = Marshal.AllocCoTaskMem(nativeSize);
674
675 lock (PatchAPIMarshaler.OldFileCountsLock)
676 {
677 PatchAPIMarshaler.OldFileCounts.Add(native, oldFileCount);
678 }
679
680 return native;
681 }
682
683 private static void ReleaseMainStruct(IntPtr native)
684 {
685 lock (PatchAPIMarshaler.OldFileCountsLock)
686 {
687 PatchAPIMarshaler.OldFileCounts.Remove(native);
688 }
689 Marshal.FreeCoTaskMem(native);
690 }
691
692 private static int GetOldFileCount(IntPtr native)
693 {
694 lock (PatchAPIMarshaler.OldFileCountsLock)
695 {
696 return PatchAPIMarshaler.OldFileCounts[native];
697 }
698 }
699
700 // Helper methods
701
702 private static IntPtr OptionalAnsiString(string managed)
703 {
704 return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemAnsi(managed);
705 }
706
707 private static IntPtr OptionalUnicodeString(string managed)
708 {
709 return (null == managed) ? IntPtr.Zero : Marshal.StringToCoTaskMemUni(managed);
710 }
711
712 // string array must be of the same length as the number of old files
713 private static IntPtr CreateArrayOfStringA(string[] managed)
714 {
715 if (null == managed)
716 {
717 return IntPtr.Zero;
718 }
719
720 int size = managed.Length * Marshal.SizeOf(typeof(IntPtr));
721 IntPtr native = Marshal.AllocCoTaskMem(size);
722
723 for (int i = 0; i < managed.Length; ++i)
724 {
725 Marshal.WriteIntPtr(native, i * Marshal.SizeOf(typeof(IntPtr)), OptionalAnsiString(managed[i]));
726 }
727
728 return native;
729 }
730
731 // string array must be of the same length as the number of old files
732 private static IntPtr CreateArrayOfStringW(string[] managed)
733 {
734 if (null == managed)
735 {
736 return IntPtr.Zero;
737 }
738
739 int size = managed.Length * Marshal.SizeOf(typeof(IntPtr));
740 IntPtr native = Marshal.AllocCoTaskMem(size);
741
742 for (int i = 0; i < managed.Length; ++i)
743 {
744 Marshal.WriteIntPtr(native, i * Marshal.SizeOf(typeof(IntPtr)), OptionalUnicodeString(managed[i]));
745 }
746
747 return native;
748 }
749
750 private static IntPtr CreateInterleaveMapRange(PatchInterleaveMap managed)
751 {
752 if (null == managed)
753 {
754 return IntPtr.Zero;
755 }
756
757 if (null == managed.ranges)
758 {
759 return IntPtr.Zero;
760 }
761
762 if (0 == managed.ranges.Length)
763 {
764 return IntPtr.Zero;
765 }
766
767 IntPtr native = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(UInt32))
768 + managed.ranges.Length * (Marshal.SizeOf(typeof(PatchInterleaveMap))));
769 WriteUInt32(native, (uint)managed.ranges.Length);
770
771 for (int i = 0; i < managed.ranges.Length; ++i)
772 {
773 Marshal.StructureToPtr(managed.ranges[i], (IntPtr)((Int64)native + i * Marshal.SizeOf(typeof(PatchInterleaveMap))), false);
774 }
775 return native;
776 }
777
778 private static IntPtr CreateInterleaveMap(PatchInterleaveMap[] managed)
779 {
780 if (null == managed)
781 {
782 return IntPtr.Zero;
783 }
784
785 IntPtr native = Marshal.AllocCoTaskMem(managed.Length * Marshal.SizeOf(typeof(IntPtr)));
786
787 for (int i = 0; i < managed.Length; ++i)
788 {
789 Marshal.WriteIntPtr(native, i * Marshal.SizeOf(typeof(IntPtr)), CreateInterleaveMapRange(managed[i]));
790 }
791
792 return native;
793 }
794
795 private static void WriteUInt32(IntPtr native, uint data)
796 {
797 Marshal.WriteInt32(native, unchecked((int)data));
798 }
799
800 private static void WriteUInt32(IntPtr native, int offset, uint data)
801 {
802 Marshal.WriteInt32(native, offset, unchecked((int)data));
803 }
804
805 // Marshal operations
806
807 private IntPtr MarshalPOD(PatchOptionData managed)
808 {
809 if (null == managed)
810 {
811 throw new ArgumentNullException("managed");
812 }
813
814 IntPtr native = this.CreateMainStruct(managed.oldFileSymbolPathArray.Length);
815 Marshal.WriteInt32(native, patchOptionDataSize); // SizeOfThisStruct
816 WriteUInt32(native, symbolOptionFlagsOffset, (uint)managed.symbolOptionFlags);
817 Marshal.WriteIntPtr(native, newFileSymbolPathOffset, PatchAPIMarshaler.OptionalAnsiString(managed.newFileSymbolPath));
818 Marshal.WriteIntPtr(native, oldFileSymbolPathArrayOffset, PatchAPIMarshaler.CreateArrayOfStringA(managed.oldFileSymbolPathArray));
819 WriteUInt32(native, extendedOptionFlagsOffset, managed.extendedOptionFlags);
820
821 // GetFunctionPointerForDelegate() throws an ArgumentNullException if the delegate is null.
822 if (null == managed.symLoadCallback)
823 {
824 Marshal.WriteIntPtr(native, symLoadCallbackOffset, IntPtr.Zero);
825 }
826 else
827 {
828 Marshal.WriteIntPtr(native, symLoadCallbackOffset, Marshal.GetFunctionPointerForDelegate(managed.symLoadCallback));
829 }
830
831 Marshal.WriteIntPtr(native, symLoadContextOffset, managed.symLoadContext);
832 Marshal.WriteIntPtr(native, interleaveMapArrayOffset, PatchAPIMarshaler.CreateInterleaveMap(managed.interleaveMapArray));
833 WriteUInt32(native, maxLzxWindowSizeOffset, managed.maxLzxWindowSize);
834 return native;
835 }
836
837 private IntPtr MarshalPOFIW_A(PatchOldFileInfoW[] managed)
838 {
839 if (null == managed)
840 {
841 throw new ArgumentNullException("managed");
842 }
843
844 if (0 == managed.Length)
845 {
846 return IntPtr.Zero;
847 }
848
849 IntPtr native = this.CreateMainStruct(managed.Length);
850
851 for (int i = 0; i < managed.Length; ++i)
852 {
853 PatchAPIMarshaler.MarshalPOFIW(managed[i], (IntPtr)((Int64)native + i * patchOldFileInfoSize));
854 }
855
856 return native;
857 }
858
859 private static void MarshalPOFIW(PatchOldFileInfoW managed, IntPtr native)
860 {
861 PatchAPIMarshaler.MarshalPOFI(managed, native);
862 Marshal.WriteIntPtr(native, oldFileOffset, PatchAPIMarshaler.OptionalUnicodeString(managed.oldFileName)); // OldFileName
863 }
864
865 private static void MarshalPOFI(PatchOldFileInfo managed, IntPtr native)
866 {
867 Marshal.WriteInt32(native, patchOldFileInfoSize); // SizeOfThisStruct
868 WriteUInt32(native, ignoreRangeCountOffset,
869 (null == managed.ignoreRange) ? 0 : (uint)managed.ignoreRange.Length); // IgnoreRangeCount // maximum 255
870 Marshal.WriteIntPtr(native, ignoreRangeArrayOffset, MarshalPIRArray(managed.ignoreRange)); // IgnoreRangeArray
871 WriteUInt32(native, retainRangeCountOffset,
872 (null == managed.retainRange) ? 0 : (uint)managed.retainRange.Length); // RetainRangeCount // maximum 255
873 Marshal.WriteIntPtr(native, retainRangeArrayOffset, MarshalPRRArray(managed.retainRange)); // RetainRangeArray
874 }
875
876 private static IntPtr MarshalPIRArray(PatchIgnoreRange[] array)
877 {
878 if (null == array)
879 {
880 return IntPtr.Zero;
881 }
882
883 if (0 == array.Length)
884 {
885 return IntPtr.Zero;
886 }
887
888 IntPtr native = Marshal.AllocCoTaskMem(array.Length * Marshal.SizeOf(typeof(PatchIgnoreRange)));
889
890 for (int i = 0; i < array.Length; ++i)
891 {
892 Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i * Marshal.SizeOf(typeof(PatchIgnoreRange)))), false);
893 }
894
895 return native;
896 }
897
898 private static IntPtr MarshalPRRArray(PatchRetainRange[] array)
899 {
900 if (null == array)
901 {
902 return IntPtr.Zero;
903 }
904
905 if (0 == array.Length)
906 {
907 return IntPtr.Zero;
908 }
909
910 IntPtr native = Marshal.AllocCoTaskMem(array.Length * Marshal.SizeOf(typeof(PatchRetainRange)));
911
912 for (int i = 0; i < array.Length; ++i)
913 {
914 Marshal.StructureToPtr(array[i], (IntPtr)((Int64)native + (i * Marshal.SizeOf(typeof(PatchRetainRange)))), false);
915 }
916
917 return native;
918 }
919
920 // CleanUp operations
921
922 private void CleanUpPOD(IntPtr native)
923 {
924 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, newFileSymbolPathOffset));
925
926 if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset))
927 {
928 for (int i = 0; i < GetOldFileCount(native); ++i)
929 {
930 Marshal.FreeCoTaskMem(
931 Marshal.ReadIntPtr(
932 Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset),
933 i * Marshal.SizeOf(typeof(IntPtr))));
934 }
935
936 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileSymbolPathArrayOffset));
937 }
938
939 if (IntPtr.Zero != Marshal.ReadIntPtr(native, interleaveMapArrayOffset))
940 {
941 for (int i = 0; i < GetOldFileCount(native); ++i)
942 {
943 Marshal.FreeCoTaskMem(
944 Marshal.ReadIntPtr(
945 Marshal.ReadIntPtr(native, interleaveMapArrayOffset),
946 i * Marshal.SizeOf(typeof(IntPtr))));
947 }
948
949 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, interleaveMapArrayOffset));
950 }
951
952 PatchAPIMarshaler.ReleaseMainStruct(native);
953 }
954
955 private void CleanUpPOFI_A(IntPtr native)
956 {
957 for (int i = 0; i < GetOldFileCount(native); ++i)
958 {
959 PatchAPIMarshaler.CleanUpPOFI((IntPtr)((Int64)native + i * patchOldFileInfoSize));
960 }
961
962 PatchAPIMarshaler.ReleaseMainStruct(native);
963 }
964
965 private static void CleanUpPOFI(IntPtr native)
966 {
967 if (IntPtr.Zero != Marshal.ReadIntPtr(native, oldFileOffset))
968 {
969 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, oldFileOffset));
970 }
971
972 PatchAPIMarshaler.CleanUpPOFIH(native);
973 }
974
975 private static void CleanUpPOFIH(IntPtr native)
976 {
977 if (IntPtr.Zero != Marshal.ReadIntPtr(native, ignoreRangeArrayOffset))
978 {
979 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, ignoreRangeArrayOffset));
980 }
981
982 if (IntPtr.Zero != Marshal.ReadIntPtr(native, retainRangeArrayOffset))
983 {
984 Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(native, retainRangeArrayOffset));
985 }
986 }
987 }
988 }
989}
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
index f63835b8..8f52bed9 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
@@ -9,7 +9,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
9 using System.IO; 9 using System.IO;
10 using System.Linq; 10 using System.Linq;
11 using WixToolset.Core.Native; 11 using WixToolset.Core.Native;
12 using WixToolset.Core.WindowsInstaller.Msi; 12 using WixToolset.Core.Native.Msi;
13 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Data.WindowsInstaller; 14 using WixToolset.Data.WindowsInstaller;
15 using WixToolset.Data.WindowsInstaller.Rows; 15 using WixToolset.Data.WindowsInstaller.Rows;
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index 9a55dc77..b510690e 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -8,7 +8,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
8 using System.Globalization; 8 using System.Globalization;
9 using System.IO; 9 using System.IO;
10 using System.Text.RegularExpressions; 10 using System.Text.RegularExpressions;
11 using WixToolset.Core.WindowsInstaller.Msi; 11 using WixToolset.Core.Native.Msi;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility.Services; 14 using WixToolset.Extensibility.Services;
@@ -288,8 +288,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
288 } 288 }
289 289
290 ColumnDefinition[] columns; 290 ColumnDefinition[] columns;
291 using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES), 291 using (Record columnNameRecord = tableView.GetColumnNames(),
292 columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES)) 292 columnTypeRecord = tableView.GetColumnTypes())
293 { 293 {
294 // index the primary keys 294 // index the primary keys
295 var tablePrimaryKeys = new HashSet<string>(); 295 var tablePrimaryKeys = new HashSet<string>();
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
index eea0fe23..75ee6307 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindMsiOrMsmCommand.cs
@@ -6,7 +6,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
6 using System.ComponentModel; 6 using System.ComponentModel;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
9 using WixToolset.Core.WindowsInstaller.Msi; 9 using WixToolset.Core.Native.Msi;
10 10
11 internal class UnbindMsiOrMsmCommand 11 internal class UnbindMsiOrMsmCommand
12 { 12 {
diff --git a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
index bde29405..9f649435 100644
--- a/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Unbind/UnbindTranformCommand.cs
@@ -9,8 +9,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
9 using System.Globalization; 9 using System.Globalization;
10 using System.IO; 10 using System.IO;
11 using System.Linq; 11 using System.Linq;
12 using WixToolset.Core.Native.Msi;
12 using WixToolset.Core.WindowsInstaller.Bind; 13 using WixToolset.Core.WindowsInstaller.Bind;
13 using WixToolset.Core.WindowsInstaller.Msi;
14 using WixToolset.Data; 14 using WixToolset.Data;
15 using WixToolset.Data.WindowsInstaller; 15 using WixToolset.Data.WindowsInstaller;
16 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
diff --git a/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs b/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
deleted file mode 100644
index 20606a77..00000000
--- a/src/WixToolset.Core.WindowsInstaller/ValidatorExtension.cs
+++ /dev/null
@@ -1,288 +0,0 @@
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.Extensibility
4{
5 using System;
6 using System.Collections;
7 using WixToolset.Data;
8 using WixToolset.Data.WindowsInstaller;
9 using WixToolset.Extensibility.Services;
10
11 /// <summary>
12 /// Base class for creating a validator extension. This default implementation
13 /// will fire and event with the ICE name and description.
14 /// </summary>
15 public class ValidatorExtension
16 {
17 private string databaseFile;
18 private Hashtable indexedSourceLineNumbers;
19 private WindowsInstallerData output;
20 private SourceLineNumber sourceLineNumbers;
21 private readonly IMessaging messaging;
22
23 /// <summary>
24 /// Instantiate a new <see cref="ValidatorExtension"/>.
25 /// </summary>
26 public ValidatorExtension(IMessaging messaging)
27 {
28 this.messaging = messaging;
29 }
30
31 /// <summary>
32 /// Gets or sets the path to the database to validate.
33 /// </summary>
34 /// <value>The path to the database to validate.</value>
35 public string DatabaseFile
36 {
37 get { return this.databaseFile; }
38 set { this.databaseFile = value; }
39 }
40
41 /// <summary>
42 /// Gets or sets the <see cref="Output"/> for finding source line information.
43 /// </summary>
44 /// <value>The <see cref="Output"/> for finding source line information.</value>
45 public WindowsInstallerData Output
46 {
47 get { return this.output; }
48 set { this.output = value; }
49 }
50
51 /// <summary>
52 /// Called at the beginning of the validation of a database file.
53 /// </summary>
54 /// <remarks>
55 /// <para>The Validator will set
56 /// <see cref="DatabaseFile"/> before calling InitializeValidator.</para>
57 /// <para><b>Notes to Inheritors:</b> When overriding
58 /// <b>InitializeValidator</b> in a derived class, be sure to call
59 /// the base class's <b>InitializeValidator</b> to thoroughly
60 /// initialize the extension.</para>
61 /// </remarks>
62 public virtual void InitializeValidator()
63 {
64 if (this.databaseFile != null)
65 {
66 this.sourceLineNumbers = new SourceLineNumber(this.databaseFile);
67 }
68 }
69
70 /// <summary>
71 /// Called at the end of the validation of a database file.
72 /// </summary>
73 /// <remarks>
74 /// <para>The default implementation will nullify source lines.</para>
75 /// <para><b>Notes to Inheritors:</b> When overriding
76 /// <b>FinalizeValidator</b> in a derived class, be sure to call
77 /// the base class's <b>FinalizeValidator</b> to thoroughly
78 /// finalize the extension.</para>
79 /// </remarks>
80 public virtual void FinalizeValidator()
81 {
82 this.sourceLineNumbers = null;
83 }
84
85 /// <summary>
86 /// Logs a message from the Validator.
87 /// </summary>
88 /// <param name="message">A <see cref="String"/> of tab-delmited tokens
89 /// in the validation message.</param>
90 public virtual void Log(string message)
91 {
92 this.Log(message, null);
93 }
94
95 /// <summary>
96 /// Logs a message from the Validator.
97 /// </summary>
98 /// <param name="message">A <see cref="String"/> of tab-delmited tokens
99 /// in the validation message.</param>
100 /// <param name="action">The name of the action to which the message
101 /// belongs.</param>
102 /// <exception cref="ArgumentNullException">The message cannot be null.
103 /// </exception>
104 /// <exception cref="WixException">The message does not contain four (4)
105 /// or more tab-delimited tokens.</exception>
106 /// <remarks>
107 /// <para><paramref name="message"/> a tab-delimited set of tokens,
108 /// formatted according to Windows Installer guidelines for ICE
109 /// message. The following table lists what each token by index
110 /// should mean.</para>
111 /// <para><paramref name="action"/> a name that represents the ICE
112 /// action that was executed (e.g. 'ICE08').</para>
113 /// <list type="table">
114 /// <listheader>
115 /// <term>Index</term>
116 /// <description>Description</description>
117 /// </listheader>
118 /// <item>
119 /// <term>0</term>
120 /// <description>Name of the ICE.</description>
121 /// </item>
122 /// <item>
123 /// <term>1</term>
124 /// <description>Message type. See the following list.</description>
125 /// </item>
126 /// <item>
127 /// <term>2</term>
128 /// <description>Detailed description.</description>
129 /// </item>
130 /// <item>
131 /// <term>3</term>
132 /// <description>Help URL or location.</description>
133 /// </item>
134 /// <item>
135 /// <term>4</term>
136 /// <description>Table name.</description>
137 /// </item>
138 /// <item>
139 /// <term>5</term>
140 /// <description>Column name.</description>
141 /// </item>
142 /// <item>
143 /// <term>6</term>
144 /// <description>This and remaining fields are primary keys
145 /// to identify a row.</description>
146 /// </item>
147 /// </list>
148 /// <para>The message types are one of the following value.</para>
149 /// <list type="table">
150 /// <listheader>
151 /// <term>Value</term>
152 /// <description>Message Type</description>
153 /// </listheader>
154 /// <item>
155 /// <term>0</term>
156 /// <description>Failure message reporting the failure of the
157 /// ICE custom action.</description>
158 /// </item>
159 /// <item>
160 /// <term>1</term>
161 /// <description>Error message reporting database authoring that
162 /// case incorrect behavior.</description>
163 /// </item>
164 /// <item>
165 /// <term>2</term>
166 /// <description>Warning message reporting database authoring that
167 /// causes incorrect behavior in certain cases. Warnings can also
168 /// report unexpected side-effects of database authoring.
169 /// </description>
170 /// </item>
171 /// <item>
172 /// <term>3</term>
173 /// <description>Informational message.</description>
174 /// </item>
175 /// </list>
176 /// </remarks>
177 public virtual void Log(string message, string action)
178 {
179 if (message == null)
180 {
181 throw new ArgumentNullException("message");
182 }
183
184 var messageParts = message.Split('\t');
185 if (3 > messageParts.Length)
186 {
187 if (null == action)
188 {
189 throw new WixException(ErrorMessages.UnexpectedExternalUIMessage(message));
190 }
191 else
192 {
193 throw new WixException(ErrorMessages.UnexpectedExternalUIMessage(message, action));
194 }
195 }
196
197 SourceLineNumber messageSourceLineNumbers;
198 if (6 < messageParts.Length)
199 {
200 var primaryKeys = new string[messageParts.Length - 6];
201
202 Array.Copy(messageParts, 6, primaryKeys, 0, primaryKeys.Length);
203
204 messageSourceLineNumbers = this.GetSourceLineNumbers(messageParts[4], primaryKeys);
205 }
206 else // use the file name as the source line information
207 {
208 messageSourceLineNumbers = this.sourceLineNumbers;
209 }
210
211 switch (messageParts[1])
212 {
213 case "0":
214 case "1":
215 this.messaging.Write(ErrorMessages.ValidationError(messageSourceLineNumbers, messageParts[0], messageParts[2]));
216 break;
217 case "2":
218 this.messaging.Write(WarningMessages.ValidationWarning(messageSourceLineNumbers, messageParts[0], messageParts[2]));
219 break;
220 case "3":
221 this.messaging.Write(VerboseMessages.ValidationInfo(messageParts[0], messageParts[2]));
222 break;
223 default:
224 throw new WixException(ErrorMessages.InvalidValidatorMessageType(messageParts[1]));
225 }
226 }
227
228 /// <summary>
229 /// Gets the source line information (if available) for a row by its table name and primary key.
230 /// </summary>
231 /// <param name="tableName">The table name of the row.</param>
232 /// <param name="primaryKeys">The primary keys of the row.</param>
233 /// <returns>The source line number information if found; null otherwise.</returns>
234 protected SourceLineNumber GetSourceLineNumbers(string tableName, string[] primaryKeys)
235 {
236 // source line information only exists if an output file was supplied
237 if (null != this.output)
238 {
239 // index the source line information if it hasn't been indexed already
240 if (null == this.indexedSourceLineNumbers)
241 {
242 this.indexedSourceLineNumbers = new Hashtable();
243
244 // index each real table
245 foreach (var table in this.output.Tables)
246 {
247 // skip unreal tables
248 if (table.Definition.Unreal)
249 {
250 continue;
251 }
252
253 // index each row
254 foreach (var row in table.Rows)
255 {
256 // skip rows that don't contain source line information
257 if (null == row.SourceLineNumbers)
258 {
259 continue;
260 }
261
262 // index the row using its table name and primary key
263 var primaryKey = row.GetPrimaryKey(';');
264 if (null != primaryKey)
265 {
266 var key = String.Concat(table.Name, ":", primaryKey);
267
268 if (this.indexedSourceLineNumbers.ContainsKey(key))
269 {
270 this.messaging.Write(WarningMessages.DuplicatePrimaryKey(row.SourceLineNumbers, primaryKey, table.Name));
271 }
272 else
273 {
274 this.indexedSourceLineNumbers.Add(key, row.SourceLineNumbers);
275 }
276 }
277 }
278 }
279 }
280
281 return (SourceLineNumber)this.indexedSourceLineNumbers[String.Concat(tableName, ":", String.Join(";", primaryKeys))];
282 }
283
284 // use the file name as the source line information
285 return this.sourceLineNumbers;
286 }
287 }
288}
diff --git a/src/WixToolset.Core/Bind/FileSystem.cs b/src/WixToolset.Core/Bind/FileSystem.cs
deleted file mode 100644
index bdd65503..00000000
--- a/src/WixToolset.Core/Bind/FileSystem.cs
+++ /dev/null
@@ -1,86 +0,0 @@
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.Bind
4{
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Runtime.InteropServices;
9 using WixToolset.Extensibility;
10
11 internal class FileSystem
12 {
13 public FileSystem(IEnumerable<ILayoutExtension> extensions)
14 {
15 this.Extensions = extensions ?? Array.Empty<ILayoutExtension>();
16 }
17
18 private IEnumerable<ILayoutExtension> Extensions { get; }
19
20 /// <summary>
21 /// Copies a file.
22 /// </summary>
23 /// <param name="source">The file to copy.</param>
24 /// <param name="destination">The destination file.</param>
25 public bool CopyFile(string source, string destination)
26 {
27 foreach (var extension in this.Extensions)
28 {
29 if (extension.CopyFile(source, destination))
30 {
31 return true;
32 }
33 }
34
35 if (File.Exists(destination))
36 {
37 File.Delete(destination);
38 }
39
40 if (!CreateHardLink(destination, source, IntPtr.Zero))
41 {
42#if DEBUG
43 int er = Marshal.GetLastWin32Error();
44#endif
45
46 File.Copy(source, destination, true);
47 }
48
49 return true;
50 }
51
52 /// <summary>
53 /// Moves a file.
54 /// </summary>
55 /// <param name="source">The file to move.</param>
56 /// <param name="destination">The destination file.</param>
57 public bool MoveFile(string source, string destination)
58 {
59 foreach (var extension in this.Extensions)
60 {
61 if (extension.MoveFile(source, destination))
62 {
63 return true;
64 }
65 }
66
67 if (File.Exists(destination))
68 {
69 File.Delete(destination);
70 }
71
72 var directory = Path.GetDirectoryName(destination);
73 if (!String.IsNullOrEmpty(directory))
74 {
75 Directory.CreateDirectory(directory);
76 }
77
78 File.Move(source, destination);
79
80 return true;
81 }
82
83 [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
84 private static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
85 }
86}
diff --git a/src/WixToolset.Core/Bind/TransferFilesCommand.cs b/src/WixToolset.Core/Bind/TransferFilesCommand.cs
index b9c54a14..9c104187 100644
--- a/src/WixToolset.Core/Bind/TransferFilesCommand.cs
+++ b/src/WixToolset.Core/Bind/TransferFilesCommand.cs
@@ -6,6 +6,7 @@ namespace WixToolset.Core.Bind
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Security.AccessControl; 8 using System.Security.AccessControl;
9 using WixToolset.Core.Native;
9 using WixToolset.Data; 10 using WixToolset.Data;
10 using WixToolset.Extensibility; 11 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data; 12 using WixToolset.Extensibility.Data;
@@ -15,23 +16,23 @@ namespace WixToolset.Core.Bind
15 { 16 {
16 public TransferFilesCommand(IMessaging messaging, IEnumerable<ILayoutExtension> extensions, IEnumerable<IFileTransfer> fileTransfers, bool suppressAclReset) 17 public TransferFilesCommand(IMessaging messaging, IEnumerable<ILayoutExtension> extensions, IEnumerable<IFileTransfer> fileTransfers, bool suppressAclReset)
17 { 18 {
18 this.FileSystem = new FileSystem(extensions); 19 this.Extensions = extensions;
19 this.Messaging = messaging; 20 this.Messaging = messaging;
20 this.FileTransfers = fileTransfers; 21 this.FileTransfers = fileTransfers;
21 this.SuppressAclReset = suppressAclReset; 22 this.SuppressAclReset = suppressAclReset;
22 } 23 }
23 24
24 private FileSystem FileSystem { get; }
25
26 private IMessaging Messaging { get; } 25 private IMessaging Messaging { get; }
27 26
27 private IEnumerable<ILayoutExtension> Extensions { get; }
28
28 private IEnumerable<IFileTransfer> FileTransfers { get; } 29 private IEnumerable<IFileTransfer> FileTransfers { get; }
29 30
30 private bool SuppressAclReset { get; } 31 private bool SuppressAclReset { get; }
31 32
32 public void Execute() 33 public void Execute()
33 { 34 {
34 List<string> destinationFiles = new List<string>(); 35 var destinationFiles = new List<string>();
35 36
36 foreach (var fileTransfer in this.FileTransfers) 37 foreach (var fileTransfer in this.FileTransfers)
37 { 38 {
@@ -42,7 +43,7 @@ namespace WixToolset.Core.Bind
42 continue; 43 continue;
43 } 44 }
44 45
45 bool retry = false; 46 var retry = false;
46 do 47 do
47 { 48 {
48 try 49 try
@@ -50,12 +51,12 @@ namespace WixToolset.Core.Bind
50 if (fileTransfer.Move) 51 if (fileTransfer.Move)
51 { 52 {
52 this.Messaging.Write(VerboseMessages.MoveFile(fileTransfer.Source, fileTransfer.Destination)); 53 this.Messaging.Write(VerboseMessages.MoveFile(fileTransfer.Source, fileTransfer.Destination));
53 this.TransferFile(true, fileTransfer.Source, fileTransfer.Destination); 54 this.MoveFile(fileTransfer.Source, fileTransfer.Destination);
54 } 55 }
55 else 56 else
56 { 57 {
57 this.Messaging.Write(VerboseMessages.CopyFile(fileTransfer.Source, fileTransfer.Destination)); 58 this.Messaging.Write(VerboseMessages.CopyFile(fileTransfer.Source, fileTransfer.Destination));
58 this.TransferFile(false, fileTransfer.Source, fileTransfer.Destination); 59 this.CopyFile(fileTransfer.Source, fileTransfer.Destination);
59 } 60 }
60 61
61 retry = false; 62 retry = false;
@@ -73,7 +74,7 @@ namespace WixToolset.Core.Bind
73 throw; 74 throw;
74 } 75 }
75 76
76 string directory = Path.GetDirectoryName(fileTransfer.Destination); 77 var directory = Path.GetDirectoryName(fileTransfer.Destination);
77 this.Messaging.Write(VerboseMessages.CreateDirectory(directory)); 78 this.Messaging.Write(VerboseMessages.CreateDirectory(directory));
78 Directory.CreateDirectory(directory); 79 Directory.CreateDirectory(directory);
79 retry = true; 80 retry = true;
@@ -91,7 +92,7 @@ namespace WixToolset.Core.Bind
91 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination)); 92 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination));
92 93
93 // try to ensure the file is not read-only 94 // try to ensure the file is not read-only
94 FileAttributes attributes = File.GetAttributes(fileTransfer.Destination); 95 var attributes = File.GetAttributes(fileTransfer.Destination);
95 try 96 try
96 { 97 {
97 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly); 98 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly);
@@ -131,7 +132,7 @@ namespace WixToolset.Core.Bind
131 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination)); 132 this.Messaging.Write(VerboseMessages.RemoveDestinationFile(fileTransfer.Destination));
132 133
133 // ensure the file is not read-only, then delete it 134 // ensure the file is not read-only, then delete it
134 FileAttributes attributes = File.GetAttributes(fileTransfer.Destination); 135 var attributes = File.GetAttributes(fileTransfer.Destination);
135 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly); 136 File.SetAttributes(fileTransfer.Destination, attributes & ~FileAttributes.ReadOnly);
136 try 137 try
137 { 138 {
@@ -161,8 +162,6 @@ namespace WixToolset.Core.Bind
161 162
162 try 163 try
163 { 164 {
164 //WixToolset.Core.Native.NativeMethods.ResetAcls(destinationFiles.ToArray(), (uint)destinationFiles.Count);
165
166 foreach (var file in destinationFiles) 165 foreach (var file in destinationFiles)
167 { 166 {
168 new FileInfo(file).SetAccessControl(aclReset); 167 new FileInfo(file).SetAccessControl(aclReset);
@@ -175,23 +174,30 @@ namespace WixToolset.Core.Bind
175 } 174 }
176 } 175 }
177 176
178 private void TransferFile(bool move, string source, string destination) 177 private void CopyFile(string source, string destination)
179 { 178 {
180 bool complete = false; 179 foreach (var extension in this.Extensions)
181
182 if (move)
183 { 180 {
184 complete = this.FileSystem.MoveFile(source, destination); 181 if (extension.CopyFile(source, destination))
185 } 182 {
186 else 183 return;
187 { 184 }
188 complete = this.FileSystem.CopyFile(source, destination);
189 } 185 }
190 186
191 if (!complete) 187 FileSystem.CopyFile(source, destination, allowHardlink: true);
188 }
189
190 private void MoveFile(string source, string destination)
191 {
192 foreach (var extension in this.Extensions)
192 { 193 {
193 throw new InvalidOperationException(); // TODO: something needs to be said here that none of the binder file managers returned a result. 194 if (extension.MoveFile(source, destination))
195 {
196 return;
197 }
194 } 198 }
199
200 FileSystem.MoveFile(source, destination);
195 } 201 }
196 } 202 }
197} 203}
diff --git a/src/WixToolset.Core/Inscriber.cs b/src/WixToolset.Core/Inscriber.cs
deleted file mode 100644
index cff2dab2..00000000
--- a/src/WixToolset.Core/Inscriber.cs
+++ /dev/null
@@ -1,437 +0,0 @@
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
4{
5 using System.IO;
6 using WixToolset.Data;
7
8 /// <summary>
9 /// Converts a wixout representation of an MSM database into a ComponentGroup the form of WiX source.
10 /// </summary>
11 internal class Inscriber
12 {
13 // <summary>
14 // Gets or sets the temp files collection.
15 // </summary>
16 // <value>The temp files collection.</value>
17 // public TempFileCollection TempFiles
18 // {
19 // get { return this.tempFiles; }
20 // set { this.tempFiles = value; }
21 // }
22
23 /// <summary>
24 /// Gets or sets the path to the temp files location.
25 /// </summary>
26 /// <value>The path to the temp files location.</value>
27 public string TempFilesLocation
28 {
29 get
30 {
31 // if (null == this.tempFiles)
32 // {
33 // return null;
34 // }
35 // else
36 // {
37 // return this.tempFiles.BasePath;
38 // }
39 return Path.GetTempPath();
40 }
41 // set
42 // {
43 // this.DeleteTempFiles();
44
45 // if (null == value)
46 // {
47 // this.tempFiles = new TempFileCollection();
48 // }
49 // else
50 // {
51 // this.tempFiles = new TempFileCollection(value);
52 // }
53
54 // // ensure the base path exists
55 // Directory.CreateDirectory(this.tempFiles.BasePath);
56 // }
57 }
58
59 /// <summary>
60 /// Extracts engine from attached container and updates engine with detached container signatures.
61 /// </summary>
62 /// <param name="bundleFile">Bundle with attached container.</param>
63 /// <param name="outputFile">Bundle engine only.</param>
64 /// <returns>True if bundle was updated.</returns>
65 public bool InscribeBundleEngine(string bundleFile, string outputFile)
66 {
67 //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_unsigned.exe");
68
69 //using (BurnReader reader = BurnReader.Open(bundleFile))
70 //using (FileStream writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
71 //{
72 // reader.Stream.Seek(0, SeekOrigin.Begin);
73
74 // byte[] buffer = new byte[4 * 1024];
75 // int total = 0;
76 // int read = 0;
77 // do
78 // {
79 // read = Math.Min(buffer.Length, (int)reader.EngineSize - total);
80
81 // read = reader.Stream.Read(buffer, 0, read);
82 // writer.Write(buffer, 0, read);
83
84 // total += read;
85 // } while (total < reader.EngineSize && 0 < read);
86
87 // if (total != reader.EngineSize)
88 // {
89 // throw new InvalidOperationException("Failed to copy engine out of bundle.");
90 // }
91
92 // // TODO: update writer with detached container signatures.
93 //}
94
95 //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
96 //if (File.Exists(outputFile))
97 //{
98 // File.Delete(outputFile);
99 //}
100 //File.Move(tempFile, outputFile);
101 //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
102
103 return true;
104 }
105
106 /// <summary>
107 /// Updates engine with attached container information and adds attached container again.
108 /// </summary>
109 /// <param name="bundleFile">Bundle with attached container.</param>
110 /// <param name="signedEngineFile">Signed bundle engine.</param>
111 /// <param name="outputFile">Signed engine with attached container.</param>
112 /// <returns>True if bundle was updated.</returns>
113 public bool InscribeBundle(string bundleFile, string signedEngineFile, string outputFile)
114 {
115 //bool inscribed = false;
116 //string tempFile = Path.Combine(this.TempFilesLocation, "bundle_engine_signed.exe");
117
118 //using (BurnReader reader = BurnReader.Open(bundleFile))
119 //{
120 // File.Copy(signedEngineFile, tempFile, true);
121
122 // // If there was an attached container on the original (unsigned) bundle, put it back.
123 // if (reader.AttachedContainerSize > 0)
124 // {
125 // reader.Stream.Seek(reader.AttachedContainerAddress, SeekOrigin.Begin);
126
127 // using (BurnWriter writer = BurnWriter.Open(tempFile))
128 // {
129 // writer.RememberThenResetSignature();
130 // writer.AppendContainer(reader.Stream, reader.AttachedContainerSize, BurnCommon.Container.Attached);
131 // inscribed = true;
132 // }
133 // }
134 //}
135
136 //Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
137 //if (File.Exists(outputFile))
138 //{
139 // File.Delete(outputFile);
140 //}
141 //File.Move(tempFile, outputFile);
142 //WixToolset.Core.Native.NativeMethods.ResetAcls(new string[] { outputFile }, 1);
143
144 //return inscribed;
145 return false;
146 }
147
148 /// <summary>
149 /// Updates database with signatures from external cabinets.
150 /// </summary>
151 /// <param name="databaseFile">Path to MSI database.</param>
152 /// <param name="outputFile">Ouput for updated MSI database.</param>
153 /// <param name="tidy">Clean up files.</param>
154 /// <returns>True if database is updated.</returns>
155 public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy)
156 {
157 //// Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
158 //bool foundUnsignedExternals = false;
159 //bool shouldCommit = false;
160
161 //FileAttributes attributes = File.GetAttributes(databaseFile);
162 //if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
163 //{
164 // this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
165 // return shouldCommit;
166 //}
167
168 //using (Database database = new Database(databaseFile, OpenDatabase.Transact))
169 //{
170 // // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
171 // int codepage = 1252;
172
173 // // list of certificates for this database (hash/identifier)
174 // Dictionary<string, string> certificates = new Dictionary<string, string>();
175
176 // // Reset the in-memory tables for this new database
177 // Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
178 // Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);
179
180 // // If any digital signature records exist that are not of the media type, preserve them
181 // if (database.TableExists("MsiDigitalSignature"))
182 // {
183 // using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
184 // {
185 // while (true)
186 // {
187 // using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
188 // {
189 // if (null == digitalSignatureRecord)
190 // {
191 // break;
192 // }
193
194 // Row digitalSignatureRow = null;
195 // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
196
197 // string table = digitalSignatureRecord.GetString(0);
198 // string signObject = digitalSignatureRecord.GetString(1);
199
200 // digitalSignatureRow[0] = table;
201 // digitalSignatureRow[1] = signObject;
202 // digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);
203
204 // if (false == digitalSignatureRecord.IsNull(3))
205 // {
206 // // Export to a file, because the MSI API's require us to provide a file path on disk
207 // string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
208 // string hashFileName = string.Concat(table, ".", signObject, ".bin");
209
210 // Directory.CreateDirectory(hashPath);
211 // hashPath = Path.Combine(hashPath, hashFileName);
212
213 // using (FileStream fs = File.Create(hashPath))
214 // {
215 // int bytesRead;
216 // byte[] buffer = new byte[1024 * 4];
217
218 // while (0 != (bytesRead = digitalSignatureRecord.GetStream(3, buffer, buffer.Length)))
219 // {
220 // fs.Write(buffer, 0, bytesRead);
221 // }
222 // }
223
224 // digitalSignatureRow[3] = hashFileName;
225 // }
226 // }
227 // }
228 // }
229 // }
230
231 // // If any digital certificates exist, extract and preserve them
232 // if (database.TableExists("MsiDigitalCertificate"))
233 // {
234 // using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
235 // {
236 // while (true)
237 // {
238 // using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
239 // {
240 // if (null == digitalCertificateRecord)
241 // {
242 // break;
243 // }
244
245 // string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate
246
247 // // Export to a file, because the MSI API's require us to provide a file path on disk
248 // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
249 // Directory.CreateDirectory(certPath);
250 // certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));
251
252 // using (FileStream fs = File.Create(certPath))
253 // {
254 // int bytesRead;
255 // byte[] buffer = new byte[1024 * 4];
256
257 // while (0 != (bytesRead = digitalCertificateRecord.GetStream(2, buffer, buffer.Length)))
258 // {
259 // fs.Write(buffer, 0, bytesRead);
260 // }
261 // }
262
263 // // Add it to our "add to MsiDigitalCertificate" table dictionary
264 // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
265 // digitalCertificateRow[0] = certificateId;
266
267 // // Now set the file path on disk where this binary stream will be picked up at import time
268 // digitalCertificateRow[1] = string.Concat(certificateId, ".cer");
269
270 // // Load the cert to get it's thumbprint
271 // X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
272 // X509Certificate2 cert2 = new X509Certificate2(cert);
273
274 // certificates.Add(cert2.Thumbprint, certificateId);
275 // }
276 // }
277 // }
278 // }
279
280 // using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
281 // {
282 // while (true)
283 // {
284 // using (Record mediaRecord = mediaView.Fetch())
285 // {
286 // if (null == mediaRecord)
287 // {
288 // break;
289 // }
290
291 // X509Certificate2 cert2 = null;
292 // Row digitalSignatureRow = null;
293
294 // string cabName = mediaRecord.GetString(4); // get the name of the cab
295 // // If there is no cabinet or it's an internal cab, skip it.
296 // if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
297 // {
298 // continue;
299 // }
300
301 // string cabId = mediaRecord.GetString(1); // get the ID of the cab
302 // string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);
303
304 // // If the cabs aren't there, throw an error but continue to catch the other errors
305 // if (!File.Exists(cabPath))
306 // {
307 // this.OnMessage(WixErrors.WixFileNotFound(cabPath));
308 // continue;
309 // }
310
311 // try
312 // {
313 // // Get the certificate from the cab
314 // X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
315 // cert2 = new X509Certificate2(signedFileCert);
316 // }
317 // catch (System.Security.Cryptography.CryptographicException e)
318 // {
319 // uint HResult = unchecked((uint)Marshal.GetHRForException(e));
320
321 // // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
322 // if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
323 // {
324 // foundUnsignedExternals = true;
325 // continue;
326 // }
327
328 // // todo: exactly which HRESULT corresponds to this issue?
329 // // If it's one of these exact platforms, warn the user that it may be due to their OS.
330 // if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
331 // (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
332 // {
333 // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
334 // }
335 // else // otherwise, generic error
336 // {
337 // this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
338 // }
339 // }
340
341 // // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
342 // if (!certificates.ContainsKey(cert2.Thumbprint))
343 // {
344 // // generate a stable identifier
345 // string certificateGeneratedId = Common.GenerateIdentifier("cer", cert2.Thumbprint);
346
347 // // Add it to our "add to MsiDigitalCertificate" table dictionary
348 // Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
349 // digitalCertificateRow[0] = certificateGeneratedId;
350
351 // // Export to a file, because the MSI API's require us to provide a file path on disk
352 // string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
353 // Directory.CreateDirectory(certPath);
354 // certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
355 // File.Delete(certPath);
356
357 // using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
358 // {
359 // writer.Write(cert2.RawData);
360 // writer.Close();
361 // }
362
363 // // Now set the file path on disk where this binary stream will be picked up at import time
364 // digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");
365
366 // certificates.Add(cert2.Thumbprint, certificateGeneratedId);
367 // }
368
369 // digitalSignatureRow = digitalSignatureTable.CreateRow(null);
370
371 // digitalSignatureRow[0] = "Media";
372 // digitalSignatureRow[1] = cabId;
373 // digitalSignatureRow[2] = certificates[cert2.Thumbprint];
374 // }
375 // }
376 // }
377
378 // if (digitalCertificateTable.Rows.Count > 0)
379 // {
380 // database.ImportTable(codepage, digitalCertificateTable, this.TempFilesLocation, true);
381 // shouldCommit = true;
382 // }
383
384 // if (digitalSignatureTable.Rows.Count > 0)
385 // {
386 // database.ImportTable(codepage, digitalSignatureTable, this.TempFilesLocation, true);
387 // shouldCommit = true;
388 // }
389
390 // // TODO: if we created the table(s), then we should add the _Validation records for them.
391
392 // certificates = null;
393
394 // // If we did find external cabs but none of them were signed, give a warning
395 // if (foundUnsignedExternals)
396 // {
397 // this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
398 // }
399
400 // if (shouldCommit)
401 // {
402 // database.Commit();
403 // }
404 //}
405
406 //return shouldCommit;
407 return false;
408 }
409
410 /// <summary>
411 /// Cleans up the temp files used by the Inscriber.
412 /// </summary>
413 /// <returns>True if all files were deleted, false otherwise.</returns>
414 public bool DeleteTempFiles()
415 {
416#if REDO_IN_NETCORE
417 if (null == this.tempFiles)
418 {
419 return true; // no work to do
420 }
421 else
422 {
423 bool deleted = Common.DeleteTempFiles(this.TempFilesLocation, this);
424
425 if (deleted)
426 {
427 ((IDisposable)this.tempFiles).Dispose();
428 this.tempFiles = null; // temp files have been deleted, no need to remember this now
429 }
430
431 return deleted;
432 }
433#endif
434 return true;
435 }
436 }
437}
diff --git a/src/WixToolset.Core/WixToolset.Core.csproj b/src/WixToolset.Core/WixToolset.Core.csproj
index 947c445f..abb9bcb8 100644
--- a/src/WixToolset.Core/WixToolset.Core.csproj
+++ b/src/WixToolset.Core/WixToolset.Core.csproj
@@ -16,6 +16,7 @@
16 <ItemGroup> 16 <ItemGroup>
17 <PackageReference Include="WixToolset.Data" Version="4.0.*" /> 17 <PackageReference Include="WixToolset.Data" Version="4.0.*" />
18 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" /> 18 <PackageReference Include="WixToolset.Extensibility" Version="4.0.*" />
19 <PackageReference Include="WixToolset.Core.Native" Version="4.0.*" />
19 </ItemGroup> 20 </ItemGroup>
20 21
21 <!-- 22 <!--