aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-10-04 10:44:09 -0700
committerRob Mensching <rob@firegiant.com>2022-10-04 14:23:47 -0700
commit9a18c230cfd88996b43c8ff7c59a195fb34ed3cf (patch)
tree262f52dbcfee8bf513450d597a589fa2686f1882
parentbfec456aae2ad7abcec0f8c4c8b2f44308e48961 (diff)
downloadwix-9a18c230cfd88996b43c8ff7c59a195fb34ed3cf.tar.gz
wix-9a18c230cfd88996b43c8ff7c59a195fb34ed3cf.tar.bz2
wix-9a18c230cfd88996b43c8ff7c59a195fb34ed3cf.zip
Use file system abstraction to handle retries automatically
Fixes 4791
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs10
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs6
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs24
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs5
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs16
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs9
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs15
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs9
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs14
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs4
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs8
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs13
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs13
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs8
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs17
-rw-r--r--src/wix/WixToolset.Core/Common.cs38
-rw-r--r--src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs22
-rw-r--r--src/wix/WixToolset.Core/LayoutCreator.cs12
24 files changed, 143 insertions, 127 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
index 26184462..cd987555 100644
--- a/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
+++ b/src/api/wix/WixToolset.Extensibility/Services/IFileSystem.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Extensibility.Services 3namespace WixToolset.Extensibility.Services
4{ 4{
5 using System; 5 using System;
6 using System.IO;
6 7
7 /// <summary> 8 /// <summary>
8 /// Abstracts basic file system operations. 9 /// Abstracts basic file system operations.
@@ -33,6 +34,15 @@ namespace WixToolset.Extensibility.Services
33 void MoveFile(string source, string destination); 34 void MoveFile(string source, string destination);
34 35
35 /// <summary> 36 /// <summary>
37 /// Opens a file.
38 /// </summary>
39 /// <param name="path">The file to open.</param>
40 /// <param name="mode">A System.IO.FileMode value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten.</param>
41 /// <param name="access">A System.IO.FileAccess value that specifies the operations that can be performed on the file.</param>
42 /// <param name="share">A System.IO.FileShare value specifying the type of access other threads have to the file.</param>
43 FileStream OpenFile(string path, FileMode mode, FileAccess access, FileShare share);
44
45 /// <summary>
36 /// Executes an action and retries on any exception a few times with short pause 46 /// Executes an action and retries on any exception a few times with short pause
37 /// between each attempt. Primarily intended for use with file system operations 47 /// between each attempt. Primarily intended for use with file system operations
38 /// that might get interrupted by external systems (usually anti-virus). 48 /// that might get interrupted by external systems (usually anti-virus).
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
index e87e32c7..acc76a3b 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -56,7 +56,7 @@ namespace WixToolset.Core.Burn.Bundles
56 /// <returns>Burn reader.</returns> 56 /// <returns>Burn reader.</returns>
57 public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe) 57 public static BurnReader Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
58 { 58 {
59 var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)); 59 var binaryReader = new BinaryReader(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete));
60 var reader = new BurnReader(messaging, fileSystem, fileExe) 60 var reader = new BurnReader(messaging, fileSystem, fileExe)
61 { 61 {
62 binaryReader = binaryReader, 62 binaryReader = binaryReader,
@@ -92,7 +92,7 @@ namespace WixToolset.Core.Burn.Bundles
92 var uxContainerSlot = this.AttachedContainers[0]; 92 var uxContainerSlot = this.AttachedContainers[0];
93 93
94 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); 94 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin);
95 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) 95 using (Stream tempCab = this.fileSystem.OpenFile(tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read))
96 { 96 {
97 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size); 97 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size);
98 } 98 }
@@ -169,7 +169,7 @@ namespace WixToolset.Core.Burn.Bundles
169 var tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab"); 169 var tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab");
170 170
171 this.binaryReader.BaseStream.Seek(nextAddress, SeekOrigin.Begin); 171 this.binaryReader.BaseStream.Seek(nextAddress, SeekOrigin.Begin);
172 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) 172 using (Stream tempCab = this.fileSystem.OpenFile(tempCabPath, FileMode.Create, FileAccess.Write, FileShare.Read))
173 { 173 {
174 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size); 174 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
175 } 175 }
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index 1f0a032e..f6282443 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -24,35 +24,33 @@ namespace WixToolset.Core.Burn.Bundles
24 { 24 {
25 private bool disposed; 25 private bool disposed;
26 private BinaryWriter binaryWriter; 26 private BinaryWriter binaryWriter;
27 private readonly IFileSystem fileSystem;
27 28
28 /// <summary> 29 private BurnWriter(IMessaging messaging, IFileSystem fileSystem, string fileExe)
29 /// Creates a BurnWriter for re-writing a PE file.
30 /// </summary>
31 /// <param name="messaging"></param>
32 /// <param name="fileExe">File to modify in-place.</param>
33 private BurnWriter(IMessaging messaging, string fileExe)
34 : base(messaging, fileExe) 30 : base(messaging, fileExe)
35 { 31 {
32 this.fileSystem = fileSystem;
36 } 33 }
37 34
38 /// <summary> 35 /// <summary>
39 /// Opens a Burn writer. 36 /// Opens a Burn writer.
40 /// </summary> 37 /// </summary>
41 /// <param name="messaging"></param> 38 /// <param name="messaging">Messaging system.</param>
39 /// <param name="fileSystem">File system abstraction.</param>
42 /// <param name="fileExe">Path to file.</param> 40 /// <param name="fileExe">Path to file.</param>
43 /// <returns>Burn writer.</returns> 41 /// <returns>Burn writer.</returns>
44 public static BurnWriter Open(IMessaging messaging, string fileExe) 42 public static BurnWriter Open(IMessaging messaging, IFileSystem fileSystem, string fileExe)
45 { 43 {
46 var writer = new BurnWriter(messaging, fileExe); 44 var writer = new BurnWriter(messaging, fileSystem, fileExe);
47 45
48 using (var binaryReader = new BinaryReader(File.Open(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))) 46 using (var binaryReader = new BinaryReader(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete)))
49 { 47 {
50 writer.Initialize(binaryReader); 48 writer.Initialize(binaryReader);
51 } 49 }
52 50
53 if (!writer.Invalid) 51 if (!writer.Invalid)
54 { 52 {
55 writer.binaryWriter = new BinaryWriter(File.Open(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete)); 53 writer.binaryWriter = new BinaryWriter(fileSystem.OpenFile(fileExe, FileMode.Open, FileAccess.ReadWrite, FileShare.Read | FileShare.Delete));
56 } 54 }
57 55
58 return writer; 56 return writer;
@@ -109,7 +107,7 @@ namespace WixToolset.Core.Burn.Bundles
109 /// <returns>true if the container data is successfully appended; false otherwise</returns> 107 /// <returns>true if the container data is successfully appended; false otherwise</returns>
110 public bool AppendContainer(string fileContainer, BurnCommon.Container container) 108 public bool AppendContainer(string fileContainer, BurnCommon.Container container)
111 { 109 {
112 using (var reader = File.OpenRead(fileContainer)) 110 using (var reader = this.fileSystem.OpenFile(fileContainer, FileMode.Open, FileAccess.Read, FileShare.Read))
113 { 111 {
114 return this.AppendContainer(reader, reader.Length, container); 112 return this.AppendContainer(reader, reader.Length, container);
115 } 113 }
@@ -158,7 +156,7 @@ namespace WixToolset.Core.Burn.Bundles
158 public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container) 156 public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container)
159 { 157 {
160 var containerCount = (uint)this.AttachedContainers.Count; 158 var containerCount = (uint)this.AttachedContainers.Count;
161 uint burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + (containerCount * sizeof(uint)); 159 var burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + (containerCount * sizeof(uint));
162 var containerSlot = new ContainerSlot((uint)containerSize); 160 var containerSlot = new ContainerSlot((uint)containerSize);
163 161
164 switch (container) 162 switch (container)
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
index fdc3dfe3..64e74bbe 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBundleExeCommand.cs
@@ -82,7 +82,7 @@ namespace WixToolset.Core.Burn.Bundles
82 82
83 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers 83 // Update the .wixburn section to point to at the UX and attached container(s) then attach the containers
84 // if they should be attached. 84 // if they should be attached.
85 using (var writer = BurnWriter.Open(this.Messaging, bundleTempPath)) 85 using (var writer = BurnWriter.Open(this.Messaging, this.FileSystem, bundleTempPath))
86 { 86 {
87 var burnStubFile = new FileInfo(bundleTempPath); 87 var burnStubFile = new FileInfo(bundleTempPath);
88 writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleSymbol.BundleId); 88 writer.InitializeBundleSectionData(burnStubFile.Length, this.BundleSymbol.BundleId);
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
index d68d372c..422ad329 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -40,7 +40,7 @@ namespace WixToolset.Core.Burn.Inscribe
40 { 40 {
41 this.FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false); 41 this.FileSystem.CopyFile(this.SignedEngineFile, tempFile, allowHardlink: false);
42 42
43 using (var writer = BurnWriter.Open(this.Messaging, tempFile)) 43 using (var writer = BurnWriter.Open(this.Messaging, this.FileSystem, tempFile))
44 { 44 {
45 inscribed = writer.ReattachContainers(reader); 45 inscribed = writer.ReattachContainers(reader);
46 } 46 }
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
index c00788ca..3fde859a 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleEngineCommand.cs
@@ -33,7 +33,7 @@ namespace WixToolset.Core.Burn.Inscribe
33 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe"); 33 var tempFile = Path.Combine(this.IntermediateFolder, "bundle_engine_unsigned.exe");
34 34
35 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath)) 35 using (var reader = BurnReader.Open(this.Messaging, this.FileSystem, this.InputFilePath))
36 using (var writer = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete)) 36 using (var writer = this.FileSystem.OpenFile(tempFile, FileMode.Create, FileAccess.Write, FileShare.Read | FileShare.Delete))
37 { 37 {
38 reader.Stream.Seek(0, SeekOrigin.Begin); 38 reader.Stream.Seek(0, SeekOrigin.Begin);
39 39
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
index 5dcfa1e6..0bb9c389 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/AssemblyNameReader.cs
@@ -11,14 +11,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
11 using System.Xml; 11 using System.Xml;
12 using System.Xml.XPath; 12 using System.Xml.XPath;
13 using WixToolset.Data; 13 using WixToolset.Data;
14 using WixToolset.Extensibility.Services;
14 15
15 internal static class AssemblyNameReader 16 internal static class AssemblyNameReader
16 { 17 {
17 public static AssemblyName ReadAssembly(SourceLineNumber sourceLineNumbers, string assemblyPath, string fileVersion) 18 public static AssemblyName ReadAssembly(IFileSystem fileSystem, SourceLineNumber sourceLineNumbers, string assemblyPath, string fileVersion)
18 { 19 {
19 try 20 try
20 { 21 {
21 using (var stream = File.OpenRead(assemblyPath)) 22 using (var stream = fileSystem.OpenFile(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read))
22 using (var peReader = new PEReader(stream)) 23 using (var peReader = new PEReader(stream))
23 { 24 {
24 var reader = peReader.GetMetadataReader(); 25 var reader = peReader.GetMetadataReader();
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 41559f8b..e3d70b70 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -28,15 +28,15 @@ namespace WixToolset.Core.WindowsInstaller.Bind
28 this.Messaging = context.ServiceProvider.GetService<IMessaging>(); 28 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
29 29
30 this.WindowsInstallerBackendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>(); 30 this.WindowsInstallerBackendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
31 31 this.FileSystem = context.ServiceProvider.GetService<IFileSystem>();
32 this.PathResolver = this.ServiceProvider.GetService<IPathResolver>(); 32 this.PathResolver = context.ServiceProvider.GetService<IPathResolver>();
33 33
34 this.CabbingThreadCount = context.CabbingThreadCount; 34 this.CabbingThreadCount = context.CabbingThreadCount;
35 this.CabCachePath = context.CabCachePath; 35 this.CabCachePath = context.CabCachePath;
36 this.DefaultCompressionLevel = context.DefaultCompressionLevel; 36 this.DefaultCompressionLevel = context.DefaultCompressionLevel;
37 this.DelayedFields = context.DelayedFields; 37 this.DelayedFields = context.DelayedFields;
38 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles; 38 this.ExpectedEmbeddedFiles = context.ExpectedEmbeddedFiles;
39 this.FileSystemManager = new FileSystemManager(context.FileSystemExtensions); 39 this.FileSystemManager = new FileSystemManager(this.FileSystem, context.FileSystemExtensions);
40 this.Intermediate = context.IntermediateRepresentation; 40 this.Intermediate = context.IntermediateRepresentation;
41 this.IntermediateFolder = context.IntermediateFolder; 41 this.IntermediateFolder = context.IntermediateFolder;
42 this.OutputPath = context.OutputPath; 42 this.OutputPath = context.OutputPath;
@@ -52,12 +52,14 @@ namespace WixToolset.Core.WindowsInstaller.Bind
52 this.BackendExtensions = backendExtension; 52 this.BackendExtensions = backendExtension;
53 } 53 }
54 54
55 public IServiceProvider ServiceProvider { get; } 55 private IServiceProvider ServiceProvider { get; }
56 56
57 private IMessaging Messaging { get; } 57 private IMessaging Messaging { get; }
58 58
59 private IWindowsInstallerBackendHelper WindowsInstallerBackendHelper { get; } 59 private IWindowsInstallerBackendHelper WindowsInstallerBackendHelper { get; }
60 60
61 private IFileSystem FileSystem { get; }
62
61 private IPathResolver PathResolver { get; } 63 private IPathResolver PathResolver { get; }
62 64
63 private int CabbingThreadCount { get; } 65 private int CabbingThreadCount { get; }
@@ -279,7 +281,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
279 281
280 // Gather information about files that do not come from merge modules. 282 // Gather information about files that do not come from merge modules.
281 { 283 {
282 var command = new UpdateFileFacadesCommand(this.Messaging, section, allFileFacades, fileFacadesFromIntermediate, variableCache, overwriteHash: true); 284 var command = new UpdateFileFacadesCommand(this.Messaging, this.FileSystem, section, allFileFacades, fileFacadesFromIntermediate, variableCache, overwriteHash: true);
283 command.Execute(); 285 command.Execute();
284 } 286 }
285 287
@@ -323,7 +325,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
323 { 325 {
324 var updatedFacades = reresolvedFiles.Select(f => allFileFacades.First(ff => ff.Id == f.Id?.Id)); 326 var updatedFacades = reresolvedFiles.Select(f => allFileFacades.First(ff => ff.Id == f.Id?.Id));
325 327
326 var command = new UpdateFileFacadesCommand(this.Messaging, section, allFileFacades, updatedFacades, variableCache, overwriteHash: false); 328 var command = new UpdateFileFacadesCommand(this.Messaging, this.FileSystem, section, allFileFacades, updatedFacades, variableCache, overwriteHash: false);
327 command.Execute(); 329 command.Execute();
328 } 330 }
329 } 331 }
@@ -461,7 +463,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
461 var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltTargetOutput); 463 var trackMsi = this.WindowsInstallerBackendHelper.TrackFile(this.OutputPath, TrackedFileType.BuiltTargetOutput);
462 trackedFiles.Add(trackMsi); 464 trackedFiles.Add(trackMsi);
463 465
464 var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false); 466 var command = new GenerateDatabaseCommand(this.Messaging, this.WindowsInstallerBackendHelper, this.FileSystem, this.FileSystemManager, data, trackMsi.Path, tableDefinitions, this.IntermediateFolder, keepAddedColumns: false, this.SuppressAddingValidationRows, useSubdirectory: false);
465 command.Execute(); 467 command.Execute();
466 468
467 trackedFiles.AddRange(command.GeneratedTemporaryFiles); 469 trackedFiles.AddRange(command.GeneratedTemporaryFiles);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
index 3d8e7595..e6ab3a28 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/BindTransformCommand.cs
@@ -13,10 +13,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
13 13
14 internal class BindTransformCommand 14 internal class BindTransformCommand
15 { 15 {
16 public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions) 16 public BindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, FileSystemManager fileSystemManager, string intermediateFolder, WindowsInstallerData transform, string outputPath, TableDefinitionCollection tableDefinitions)
17 { 17 {
18 this.Messaging = messaging; 18 this.Messaging = messaging;
19 this.BackendHelper = backendHelper; 19 this.BackendHelper = backendHelper;
20 this.FileSystem = fileSystem;
20 this.FileSystemManager = fileSystemManager; 21 this.FileSystemManager = fileSystemManager;
21 this.IntermediateFolder = intermediateFolder; 22 this.IntermediateFolder = intermediateFolder;
22 this.Transform = transform; 23 this.Transform = transform;
@@ -28,6 +29,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
28 29
29 private IBackendHelper BackendHelper { get; } 30 private IBackendHelper BackendHelper { get; }
30 31
32 private IFileSystem FileSystem { get; }
33
31 private FileSystemManager FileSystemManager { get; } 34 private FileSystemManager FileSystemManager { get; }
32 35
33 private TableDefinitionCollection TableDefinitions { get; } 36 private TableDefinitionCollection TableDefinitions { get; }
@@ -398,7 +401,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
398 { 401 {
399 if (!String.IsNullOrEmpty(emptyFile)) 402 if (!String.IsNullOrEmpty(emptyFile))
400 { 403 {
401 using (var fileStream = File.Create(emptyFile)) 404 using (var fileStream = this.FileSystem.OpenFile(emptyFile, FileMode.Create, FileAccess.Write, FileShare.None))
402 { 405 {
403 } 406 }
404 } 407 }
@@ -434,7 +437,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
434 437
435 private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns) 438 private void GenerateDatabase(WindowsInstallerData output, string outputPath, bool keepAddedColumns)
436 { 439 {
437 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true); 440 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.FileSystemManager, output, outputPath, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns, suppressAddingValidationRows: true, useSubdirectory: true);
438 command.Execute(); 441 command.Execute();
439 } 442 }
440 } 443 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
index 0d88cfd1..1f6b6558 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
@@ -16,10 +16,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
16 16
17 internal class CreatePatchTransformsCommand 17 internal class CreatePatchTransformsCommand
18 { 18 {
19 public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection<IResolverExtension> resolverExtensions, IReadOnlyCollection<IWindowsInstallerBackendBinderExtension> backendExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection<IBindPath> bindPaths) 19 public CreatePatchTransformsCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver, IFileResolver fileResolver, IReadOnlyCollection<IResolverExtension> resolverExtensions, IReadOnlyCollection<IWindowsInstallerBackendBinderExtension> backendExtensions, Intermediate intermediate, string intermediateFolder, IReadOnlyCollection<IBindPath> bindPaths)
20 { 20 {
21 this.Messaging = messaging; 21 this.Messaging = messaging;
22 this.BackendHelper = backendHelper; 22 this.BackendHelper = backendHelper;
23 this.FileSystem = fileSystem;
23 this.PathResolver = pathResolver; 24 this.PathResolver = pathResolver;
24 this.FileResolver = fileResolver; 25 this.FileResolver = fileResolver;
25 this.ResolverExtensions = resolverExtensions; 26 this.ResolverExtensions = resolverExtensions;
@@ -33,6 +34,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
33 34
34 private IBackendHelper BackendHelper { get; } 35 private IBackendHelper BackendHelper { get; }
35 36
37 private IFileSystem FileSystem { get; }
38
36 private IPathResolver PathResolver { get; } 39 private IPathResolver PathResolver { get; }
37 40
38 private IFileResolver FileResolver { get; } 41 private IFileResolver FileResolver { get; }
@@ -105,7 +108,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
105 var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder); 108 var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder);
106 var extractFilesFolder = Path.Combine(exportBasePath, "File"); 109 var extractFilesFolder = Path.Combine(exportBasePath, "File");
107 110
108 var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, null, OutputType.Product, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); 111 var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.PathResolver, path, null, OutputType.Product, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false);
109 data = command.Execute(); 112 data = command.Execute();
110 } 113 }
111 114
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
index fe65ccef..8707d5f1 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/FileSystemManager.cs
@@ -5,15 +5,20 @@ namespace WixToolset.Core.WindowsInstaller.Bind
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Runtime.CompilerServices;
8 using WixToolset.Extensibility; 9 using WixToolset.Extensibility;
10 using WixToolset.Extensibility.Services;
9 11
10 internal class FileSystemManager 12 internal class FileSystemManager
11 { 13 {
12 public FileSystemManager(IEnumerable<IFileSystemExtension> fileSystemExtensions) 14 public FileSystemManager(IFileSystem fileSystem, IEnumerable<IFileSystemExtension> fileSystemExtensions)
13 { 15 {
16 this.FileSystem = fileSystem;
14 this.Extensions = fileSystemExtensions; 17 this.Extensions = fileSystemExtensions;
15 } 18 }
16 19
20 private IFileSystem FileSystem { get; }
21
17 private IEnumerable<IFileSystemExtension> Extensions { get; } 22 private IEnumerable<IFileSystemExtension> Extensions { get; }
18 23
19 public bool CompareFiles(string firstPath, string secondPath) 24 public bool CompareFiles(string firstPath, string secondPath)
@@ -27,18 +32,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind
27 } 32 }
28 } 33 }
29 34
30 return BuiltinCompareFiles(firstPath, secondPath); 35 return this.BuiltinCompareFiles(firstPath, secondPath);
31 } 36 }
32 37
33 private static bool BuiltinCompareFiles(string firstPath, string secondPath) 38 private bool BuiltinCompareFiles(string firstPath, string secondPath)
34 { 39 {
35 if (String.Equals(firstPath, secondPath, StringComparison.OrdinalIgnoreCase)) 40 if (String.Equals(firstPath, secondPath, StringComparison.OrdinalIgnoreCase))
36 { 41 {
37 return true; 42 return true;
38 } 43 }
39 44
40 using (var firstStream = File.OpenRead(firstPath)) 45 using (var firstStream = this.FileSystem.OpenFile(firstPath, FileMode.Open, FileAccess.Read, FileShare.Read))
41 using (var secondStream = File.OpenRead(secondPath)) 46 using (var secondStream = this.FileSystem.OpenFile(secondPath, FileMode.Open, FileAccess.Read, FileShare.Read))
42 { 47 {
43 if (firstStream.Length != secondStream.Length) 48 if (firstStream.Length != secondStream.Length)
44 { 49 {
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
index b8cca752..361f797d 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/GenerateDatabaseCommand.cs
@@ -18,10 +18,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
18 { 18 {
19 private const string IdtsSubFolder = "_idts"; 19 private const string IdtsSubFolder = "_idts";
20 20
21 public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory) 21 public GenerateDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, FileSystemManager fileSystemManager, WindowsInstallerData data, string outputPath, TableDefinitionCollection tableDefinitions, string intermediateFolder, bool keepAddedColumns, bool suppressAddingValidationRows, bool useSubdirectory)
22 { 22 {
23 this.Messaging = messaging; 23 this.Messaging = messaging;
24 this.BackendHelper = backendHelper; 24 this.BackendHelper = backendHelper;
25 this.FileSystem = fileSystem;
25 this.FileSystemManager = fileSystemManager; 26 this.FileSystemManager = fileSystemManager;
26 this.Data = data; 27 this.Data = data;
27 this.OutputPath = outputPath; 28 this.OutputPath = outputPath;
@@ -34,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
34 35
35 private IBackendHelper BackendHelper { get; } 36 private IBackendHelper BackendHelper { get; }
36 37
38 private IFileSystem FileSystem { get; }
39
37 private FileSystemManager FileSystemManager { get; } 40 private FileSystemManager FileSystemManager { get; }
38 41
39 /// <summary> 42 /// <summary>
@@ -358,7 +361,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
358 var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst")); 361 var transformFile = Path.Combine(this.IntermediateFolder, String.Concat(subStorage.Name, ".mst"));
359 362
360 // Bind the transform. 363 // Bind the transform.
361 var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions); 364 var command = new BindTransformCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.FileSystemManager, this.IntermediateFolder, subStorage.Data, transformFile, this.TableDefinitions);
362 command.Execute(); 365 command.Execute();
363 366
364 if (this.Messaging.EncounteredError) 367 if (this.Messaging.EncounteredError)
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
index f2f8e126..7f764f49 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/UpdateFileFacadesCommand.cs
@@ -19,9 +19,10 @@ namespace WixToolset.Core.WindowsInstaller.Bind
19 /// </summary> 19 /// </summary>
20 internal class UpdateFileFacadesCommand 20 internal class UpdateFileFacadesCommand
21 { 21 {
22 public UpdateFileFacadesCommand(IMessaging messaging, IntermediateSection section, IEnumerable<IFileFacade> allFileFacades, IEnumerable<IFileFacade> updateFileFacades, IDictionary<string, string> variableCache, bool overwriteHash) 22 public UpdateFileFacadesCommand(IMessaging messaging, IFileSystem fileSystem, IntermediateSection section, IEnumerable<IFileFacade> allFileFacades, IEnumerable<IFileFacade> updateFileFacades, IDictionary<string, string> variableCache, bool overwriteHash)
23 { 23 {
24 this.Messaging = messaging; 24 this.Messaging = messaging;
25 this.FileSystem = fileSystem;
25 this.Section = section; 26 this.Section = section;
26 this.AllFileFacades = allFileFacades; 27 this.AllFileFacades = allFileFacades;
27 this.UpdateFileFacades = updateFileFacades; 28 this.UpdateFileFacades = updateFileFacades;
@@ -31,6 +32,8 @@ namespace WixToolset.Core.WindowsInstaller.Bind
31 32
32 private IMessaging Messaging { get; } 33 private IMessaging Messaging { get; }
33 34
35 private IFileSystem FileSystem { get; }
36
34 private IntermediateSection Section { get; } 37 private IntermediateSection Section { get; }
35 38
36 private IEnumerable<IFileFacade> AllFileFacades { get; } 39 private IEnumerable<IFileFacade> AllFileFacades { get; }
@@ -212,7 +215,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
212 { 215 {
213 try 216 try
214 { 217 {
215 var assemblyName = AssemblyNameReader.ReadAssembly(facade.SourceLineNumber, fileInfo.FullName, version); 218 var assemblyName = AssemblyNameReader.ReadAssembly(this.FileSystem, facade.SourceLineNumber, fileInfo.FullName, version);
216 219
217 this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name); 220 this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "name", assemblyName.Name);
218 this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "culture", assemblyName.Culture); 221 this.SetMsiAssemblyName(assemblyNameSymbols, facade, assemblySymbol, "culture", assemblyName.Culture);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
index e37267bb..3b582e74 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
@@ -21,6 +21,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
21 { 21 {
22 this.Messaging = serviceProvider.GetService<IMessaging>(); 22 this.Messaging = serviceProvider.GetService<IMessaging>();
23 this.BackendHelper = serviceProvider.GetService<IBackendHelper>(); 23 this.BackendHelper = serviceProvider.GetService<IBackendHelper>();
24 this.FileSystem = serviceProvider.GetService<IFileSystem>();
24 this.PathResolver = serviceProvider.GetService<IPathResolver>(); 25 this.PathResolver = serviceProvider.GetService<IPathResolver>();
25 this.ExtensionManager = serviceProvider.GetService<IExtensionManager>(); 26 this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
26 } 27 }
@@ -29,6 +30,8 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
29 30
30 private IBackendHelper BackendHelper { get; } 31 private IBackendHelper BackendHelper { get; }
31 32
33 private IFileSystem FileSystem { get; }
34
32 private IPathResolver PathResolver { get; } 35 private IPathResolver PathResolver { get; }
33 36
34 private IExtensionManager ExtensionManager { get; } 37 private IExtensionManager ExtensionManager { get; }
@@ -342,11 +345,11 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
342 else 345 else
343 { 346 {
344 var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>(); 347 var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
345 var fileSystemManager = new FileSystemManager(fileSystemExtensions); 348 var fileSystemManager = new FileSystemManager(this.FileSystem, fileSystemExtensions);
346 349
347 var tableDefinitions = this.GetTableDefinitions(); 350 var tableDefinitions = this.GetTableDefinitions();
348 351
349 var bindCommand = new BindTransformCommand(this.Messaging, this.BackendHelper, fileSystemManager, this.IntermediateFolder, transform, this.OutputPath, tableDefinitions); 352 var bindCommand = new BindTransformCommand(this.Messaging, this.BackendHelper, this.FileSystem, fileSystemManager, this.IntermediateFolder, transform, this.OutputPath, tableDefinitions);
350 bindCommand.Execute(); 353 bindCommand.Execute();
351 } 354 }
352 } 355 }
@@ -379,7 +382,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
379 { 382 {
380 if (!DataLoader.TryLoadWindowsInstallerData(path, out var data)) 383 if (!DataLoader.TryLoadWindowsInstallerData(path, out var data))
381 { 384 {
382 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, null, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); 385 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.PathResolver, path, null, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false);
383 data = unbindCommand.Execute(); 386 data = unbindCommand.Execute();
384 } 387 }
385 388
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
index 80fcb8e1..3f0d0c51 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Inscribe/InscribeMsiPackageCommand.cs
@@ -19,6 +19,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
19 public InscribeMsiPackageCommand(IServiceProvider serviceProvider, string inputPath, string intermediateFolder, string outputPath) 19 public InscribeMsiPackageCommand(IServiceProvider serviceProvider, string inputPath, string intermediateFolder, string outputPath)
20 { 20 {
21 this.Messaging = serviceProvider.GetService<IMessaging>(); 21 this.Messaging = serviceProvider.GetService<IMessaging>();
22 this.FileSystem = serviceProvider.GetService<IFileSystem>();
22 this.WindowsInstallerBackendHelper = serviceProvider.GetService<IWindowsInstallerBackendHelper>(); 23 this.WindowsInstallerBackendHelper = serviceProvider.GetService<IWindowsInstallerBackendHelper>();
23 this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); 24 this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
24 this.InputPath = inputPath; 25 this.InputPath = inputPath;
@@ -34,6 +35,8 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
34 35
35 private IMessaging Messaging { get; } 36 private IMessaging Messaging { get; }
36 37
38 private IFileSystem FileSystem { get; }
39
37 private IWindowsInstallerBackendHelper WindowsInstallerBackendHelper { get; } 40 private IWindowsInstallerBackendHelper WindowsInstallerBackendHelper { get; }
38 41
39 private TableDefinitionCollection TableDefinitions { get; } 42 private TableDefinitionCollection TableDefinitions { get; }
@@ -48,8 +51,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
48 51
49 if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase)) 52 if (!String.Equals(this.InputPath, this.OutputPath, StringComparison.OrdinalIgnoreCase))
50 { 53 {
51 Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath)); 54 this.FileSystem.CopyFile(this.InputPath, this.OutputPath, allowHardlink: false);
52 File.Copy(this.InputPath, this.OutputPath, true);
53 } 55 }
54 56
55 var attributes = File.GetAttributes(databasePath); 57 var attributes = File.GetAttributes(databasePath);
@@ -96,7 +98,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
96 Directory.CreateDirectory(hashPath); 98 Directory.CreateDirectory(hashPath);
97 hashPath = Path.Combine(hashPath, hashFileName); 99 hashPath = Path.Combine(hashPath, hashFileName);
98 100
99 using (var fs = File.Create(hashPath)) 101 using (var fs = this.FileSystem.OpenFile(hashPath, FileMode.Create, FileAccess.Write, FileShare.None))
100 { 102 {
101 int bytesRead; 103 int bytesRead;
102 var buffer = new byte[1024 * 4]; 104 var buffer = new byte[1024 * 4];
@@ -127,7 +129,7 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
127 Directory.CreateDirectory(certPath); 129 Directory.CreateDirectory(certPath);
128 certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer")); 130 certPath = Path.Combine(certPath, String.Concat(certificateId, ".cer"));
129 131
130 using (var fs = File.Create(certPath)) 132 using (var fs = this.FileSystem.OpenFile(certPath, FileMode.Create, FileAccess.Write, FileShare.None))
131 { 133 {
132 int bytesRead; 134 int bytesRead;
133 var buffer = new byte[1024 * 4]; 135 var buffer = new byte[1024 * 4];
@@ -221,9 +223,9 @@ namespace WixToolset.Core.WindowsInstaller.Inscribe
221 var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate"); 223 var certPath = Path.Combine(this.IntermediateFolder, "MsiDigitalCertificate");
222 Directory.CreateDirectory(certPath); 224 Directory.CreateDirectory(certPath);
223 certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer")); 225 certPath = Path.Combine(certPath, String.Concat(cert2.Thumbprint, ".cer"));
224 File.Delete(certPath); 226 this.FileSystem.DeleteFile(certPath, true);
225 227
226 using (var writer = new BinaryWriter(File.Open(certPath, FileMode.Create))) 228 using (var writer = new BinaryWriter(this.FileSystem.OpenFile(certPath, FileMode.Create, FileAccess.Write, FileShare.Read)))
227 { 229 {
228 writer.Write(cert2.RawData); 230 writer.Write(cert2.RawData);
229 writer.Close(); 231 writer.Close();
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
index ace382de..8320bb0a 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
@@ -17,6 +17,8 @@ namespace WixToolset.Core.WindowsInstaller
17 17
18 var backendHelper = context.ServiceProvider.GetService<IBackendHelper>(); 18 var backendHelper = context.ServiceProvider.GetService<IBackendHelper>();
19 19
20 var fileSystem = context.ServiceProvider.GetService<IFileSystem>();
21
20 var pathResolver = context.ServiceProvider.GetService<IPathResolver>(); 22 var pathResolver = context.ServiceProvider.GetService<IPathResolver>();
21 23
22 var fileResolver = context.ServiceProvider.GetService<IFileResolver>(); 24 var fileResolver = context.ServiceProvider.GetService<IFileResolver>();
@@ -36,7 +38,7 @@ namespace WixToolset.Core.WindowsInstaller
36 IEnumerable<PatchTransform> patchTransforms; 38 IEnumerable<PatchTransform> patchTransforms;
37 PatchFilterMap patchFilterMap; 39 PatchFilterMap patchFilterMap;
38 { 40 {
39 var command = new CreatePatchTransformsCommand(messaging, backendHelper, pathResolver, fileResolver, resolveExtensions, backendExtensions, context.IntermediateRepresentation, context.IntermediateFolder, context.BindPaths); 41 var command = new CreatePatchTransformsCommand(messaging, backendHelper, fileSystem, pathResolver, fileResolver, resolveExtensions, backendExtensions, context.IntermediateRepresentation, context.IntermediateFolder, context.BindPaths);
40 command.Execute(); 42 command.Execute();
41 43
42 patchTransforms = command.PatchTransforms; 44 patchTransforms = command.PatchTransforms;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
index 9aa6f3d4..c2981b08 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/ExtractCabinetsCommand.cs
@@ -12,11 +12,13 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Data.WindowsInstaller.Rows; 14 using WixToolset.Data.WindowsInstaller.Rows;
15 using WixToolset.Extensibility.Services;
15 16
16 internal class ExtractCabinetsCommand 17 internal class ExtractCabinetsCommand
17 { 18 {
18 public ExtractCabinetsCommand(WindowsInstallerData output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder, bool treatOutputAsModule = false) 19 public ExtractCabinetsCommand(IFileSystem fileSystem, WindowsInstallerData output, Database database, string inputFilePath, string exportBasePath, string intermediateFolder, bool treatOutputAsModule = false)
19 { 20 {
21 this.FileSystem = fileSystem;
20 this.Output = output; 22 this.Output = output;
21 this.Database = database; 23 this.Database = database;
22 this.InputFilePath = inputFilePath; 24 this.InputFilePath = inputFilePath;
@@ -27,6 +29,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
27 29
28 public Dictionary<string, MediaRow> ExtractedFileIdsWithMediaRow { get; private set; } 30 public Dictionary<string, MediaRow> ExtractedFileIdsWithMediaRow { get; private set; }
29 31
32 private IFileSystem FileSystem { get; }
33
30 private WindowsInstallerData Output { get; } 34 private WindowsInstallerData Output { get; }
31 35
32 private Database Database { get; } 36 private Database Database { get; }
@@ -100,7 +104,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
100 // ensure the parent directory exists 104 // ensure the parent directory exists
101 Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath)); 105 Directory.CreateDirectory(Path.GetDirectoryName(cabinetPath));
102 106
103 using (var fs = File.Create(cabinetPath)) 107 using (var fs = this.FileSystem.OpenFile(cabinetPath, FileMode.Create, FileAccess.Write, FileShare.None))
104 { 108 {
105 int bytesRead; 109 int bytesRead;
106 var buffer = new byte[4096]; 110 var buffer = new byte[4096];
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index 7f6537e5..54534e50 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -21,10 +21,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
21 { 21 {
22 private static readonly Regex Modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); 22 private static readonly Regex Modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
23 23
24 public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, string databasePath, Database database, OutputType outputType, string exportBasePath, string extractFilesFolder, string intermediateFolder, bool enableDemodularization, bool skipSummaryInfo) 24 public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver, string databasePath, Database database, OutputType outputType, string exportBasePath, string extractFilesFolder, string intermediateFolder, bool enableDemodularization, bool skipSummaryInfo)
25 { 25 {
26 this.Messaging = messaging; 26 this.Messaging = messaging;
27 this.BackendHelper = backendHelper; 27 this.BackendHelper = backendHelper;
28 this.FileSystem = fileSystem;
28 this.PathResolver = pathResolver; 29 this.PathResolver = pathResolver;
29 this.DatabasePath = databasePath; 30 this.DatabasePath = databasePath;
30 this.Database = database; 31 this.Database = database;
@@ -38,9 +39,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
38 this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All); 39 this.TableDefinitions = new TableDefinitionCollection(WindowsInstallerTableDefinitions.All);
39 } 40 }
40 41
41 public IMessaging Messaging { get; } 42 private IMessaging Messaging { get; }
42 43
43 public IBackendHelper BackendHelper { get; } 44 private IBackendHelper BackendHelper { get; }
45
46 private IFileSystem FileSystem { get; }
44 47
45 private IPathResolver PathResolver { get; } 48 private IPathResolver PathResolver { get; }
46 49
@@ -203,7 +206,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
203 206
204 Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); 207 Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName));
205 208
206 using (var fs = File.Create(source)) 209 using (var fs = this.FileSystem.OpenFile(source, FileMode.Create, FileAccess.Write, FileShare.None))
207 { 210 {
208 int bytesRead; 211 int bytesRead;
209 var buffer = new byte[4096]; 212 var buffer = new byte[4096];
@@ -531,7 +534,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
531 534
532 if (!String.IsNullOrEmpty(this.ExtractFilesFolder)) 535 if (!String.IsNullOrEmpty(this.ExtractFilesFolder))
533 { 536 {
534 var extractCommand = new ExtractCabinetsCommand(output, this.Database, this.DatabasePath, this.ExtractFilesFolder, this.IntermediateFolder); 537 var extractCommand = new ExtractCabinetsCommand(this.FileSystem, output, this.Database, this.DatabasePath, this.ExtractFilesFolder, this.IntermediateFolder);
535 extractCommand.Execute(); 538 extractCommand.Execute();
536 539
537 extractedFileIds = new HashSet<string>(extractCommand.ExtractedFileIdsWithMediaRow.Keys, StringComparer.OrdinalIgnoreCase); 540 extractedFileIds = new HashSet<string>(extractCommand.ExtractedFileIdsWithMediaRow.Keys, StringComparer.OrdinalIgnoreCase);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
index f2567ebb..bb4da1ed 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
@@ -16,10 +16,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
16 16
17 internal class UnbindTransformCommand 17 internal class UnbindTransformCommand
18 { 18 {
19 public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, FileSystemManager fileSystemManager, string transformFile, string exportBasePath, string intermediateFolder) 19 public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver, FileSystemManager fileSystemManager, string transformFile, string exportBasePath, string intermediateFolder)
20 { 20 {
21 this.Messaging = messaging; 21 this.Messaging = messaging;
22 this.BackendHelper = backendHelper; 22 this.BackendHelper = backendHelper;
23 this.FileSystem = fileSystem;
23 this.PathResolver = pathResolver; 24 this.PathResolver = pathResolver;
24 this.FileSystemManager = fileSystemManager; 25 this.FileSystemManager = fileSystemManager;
25 this.TransformFile = transformFile; 26 this.TransformFile = transformFile;
@@ -33,6 +34,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
33 34
34 private IBackendHelper BackendHelper { get; } 35 private IBackendHelper BackendHelper { get; }
35 36
37 private IFileSystem FileSystem { get; }
38
36 private IPathResolver PathResolver { get; } 39 private IPathResolver PathResolver { get; }
37 40
38 private FileSystemManager FileSystemManager { get; } 41 private FileSystemManager FileSystemManager { get; }
@@ -116,7 +119,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
116 using (var msiDatabase = this.ApplyTransformToSchemaDatabase(schemaDatabasePath, TransformErrorConditions.All | TransformErrorConditions.ViewTransform)) 119 using (var msiDatabase = this.ApplyTransformToSchemaDatabase(schemaDatabasePath, TransformErrorConditions.All | TransformErrorConditions.ViewTransform))
117 { 120 {
118 // unbind the database 121 // unbind the database
119 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, schemaDatabasePath, msiDatabase, OutputType.Product, null, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); 122 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.PathResolver, schemaDatabasePath, msiDatabase, OutputType.Product, null, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true);
120 var transformViewOutput = unbindCommand.Execute(); 123 var transformViewOutput = unbindCommand.Execute();
121 124
122 return transformViewOutput.Tables["_TransformView"]; 125 return transformViewOutput.Tables["_TransformView"];
@@ -177,7 +180,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
177 { 180 {
178 181
179 // unbind the database 182 // unbind the database
180 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, schemaDatabasePath, database, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); 183 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.PathResolver, schemaDatabasePath, database, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true);
181 output = unbindCommand.Execute(); 184 output = unbindCommand.Execute();
182 } 185 }
183 186
@@ -324,7 +327,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
324 if (null == this.EmptyFile) 327 if (null == this.EmptyFile)
325 { 328 {
326 this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty"); 329 this.EmptyFile = Path.Combine(this.IntermediateFolder, ".empty");
327 using (var fileStream = File.Create(this.EmptyFile)) 330 using (var fileStream = this.FileSystem.OpenFile(this.EmptyFile, FileMode.Create, FileAccess.Write, FileShare.None))
328 { 331 {
329 } 332 }
330 } 333 }
@@ -343,7 +346,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
343 346
344 private void GenerateDatabase(WindowsInstallerData data) 347 private void GenerateDatabase(WindowsInstallerData data)
345 { 348 {
346 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, data, data.SourceLineNumbers.FileName, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); 349 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystem, this.FileSystemManager, data, data.SourceLineNumbers.FileName, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false);
347 command.Execute(); 350 command.Execute();
348 } 351 }
349 352
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
index 06fa6817..24dd9927 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Validate/ValidateDatabaseCommand.cs
@@ -34,9 +34,6 @@ namespace WixToolset.Core.WindowsInstaller.Validate
34 34
35 public IEnumerable<ITrackedFile> TrackedFiles { get; private set; } 35 public IEnumerable<ITrackedFile> TrackedFiles { get; private set; }
36 36
37 /// <summary>
38 /// Encountered error implementation for <see cref="IWindowsInstallerValidatorCallback"/>.
39 /// </summary>
40 public bool EncounteredError => this.Messaging.EncounteredError; 37 public bool EncounteredError => this.Messaging.EncounteredError;
41 38
42 private IMessaging Messaging { get; } 39 private IMessaging Messaging { get; }
@@ -84,10 +81,7 @@ namespace WixToolset.Core.WindowsInstaller.Validate
84 } 81 }
85 finally 82 finally
86 { 83 {
87 if (File.Exists(workingDatabasePath)) 84 this.FileSystem.DeleteFile(workingDatabasePath);
88 {
89 File.Delete(workingDatabasePath);
90 }
91 } 85 }
92 86
93 stopwatch.Stop(); 87 stopwatch.Stop();
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
index f3d116f6..b41876b3 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
@@ -3,7 +3,6 @@
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic;
7 using System.IO; 6 using System.IO;
8 using System.Linq; 7 using System.Linq;
9 using WixToolset.Core.WindowsInstaller.Bind; 8 using WixToolset.Core.WindowsInstaller.Bind;
@@ -74,25 +73,27 @@ namespace WixToolset.Core.WindowsInstaller
74 73
75 var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>(); 74 var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
76 75
76 var fileSystem = context.ServiceProvider.GetService<IFileSystem>();
77
77 var pathResolver = context.ServiceProvider.GetService<IPathResolver>(); 78 var pathResolver = context.ServiceProvider.GetService<IPathResolver>();
78 79
79 if (context.DecompileType == OutputType.Transform) 80 if (context.DecompileType == OutputType.Transform)
80 { 81 {
81 return this.DecompileTransform(context, backendHelper, pathResolver); 82 return this.DecompileTransform(context, backendHelper, fileSystem, pathResolver);
82 } 83 }
83 else 84 else
84 { 85 {
85 return this.DecompileDatabase(context, backendHelper, pathResolver); 86 return this.DecompileDatabase(context, backendHelper, fileSystem, pathResolver);
86 } 87 }
87 } 88 }
88 89
89 private IWindowsInstallerDecompileResult DecompileDatabase(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IPathResolver pathResolver) 90 private IWindowsInstallerDecompileResult DecompileDatabase(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver)
90 { 91 {
91 var extractFilesFolder = context.SuppressExtractCabinets || (String.IsNullOrEmpty(context.CabinetExtractFolder) && String.IsNullOrEmpty(context.ExtractFolder)) ? null : 92 var extractFilesFolder = context.SuppressExtractCabinets || (String.IsNullOrEmpty(context.CabinetExtractFolder) && String.IsNullOrEmpty(context.ExtractFolder)) ? null :
92 String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder; 93 String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder;
93 94
94 var outputType = context.TreatProductAsModule ? OutputType.Module : context.DecompileType; 95 var outputType = context.TreatProductAsModule ? OutputType.Module : context.DecompileType;
95 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, pathResolver, context.DecompilePath, null, outputType, context.ExtractFolder, extractFilesFolder, context.IntermediateFolder, enableDemodularization: true, skipSummaryInfo: false); 96 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, fileSystem, pathResolver, context.DecompilePath, null, outputType, context.ExtractFolder, extractFilesFolder, context.IntermediateFolder, enableDemodularization: true, skipSummaryInfo: false);
96 var output = unbindCommand.Execute(); 97 var output = unbindCommand.Execute();
97 var extractedFilePaths = unbindCommand.ExportedFiles; 98 var extractedFilePaths = unbindCommand.ExportedFiles;
98 99
@@ -108,13 +109,13 @@ namespace WixToolset.Core.WindowsInstaller
108 return result; 109 return result;
109 } 110 }
110 111
111 private IWindowsInstallerDecompileResult DecompileTransform(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IPathResolver pathResolver) 112 private IWindowsInstallerDecompileResult DecompileTransform(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IFileSystem fileSystem, IPathResolver pathResolver)
112 { 113 {
113 var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>(); 114 var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
114 115
115 var fileSystemManager = new FileSystemManager(fileSystemExtensions); 116 var fileSystemManager = new FileSystemManager(fileSystem, fileSystemExtensions);
116 117
117 var unbindCommand = new UnbindTransformCommand(this.Messaging, backendHelper, pathResolver, fileSystemManager, context.DecompilePath, context.ExtractFolder, context.IntermediateFolder); 118 var unbindCommand = new UnbindTransformCommand(this.Messaging, backendHelper, fileSystem, pathResolver, fileSystemManager, context.DecompilePath, context.ExtractFolder, context.IntermediateFolder);
118 var output = unbindCommand.Execute(); 119 var output = unbindCommand.Execute();
119 120
120 var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>(); 121 var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
diff --git a/src/wix/WixToolset.Core/Common.cs b/src/wix/WixToolset.Core/Common.cs
index f581e1a4..f5a9709d 100644
--- a/src/wix/WixToolset.Core/Common.cs
+++ b/src/wix/WixToolset.Core/Common.cs
@@ -362,44 +362,6 @@ namespace WixToolset.Core
362 } 362 }
363 363
364 /// <summary> 364 /// <summary>
365 /// Recursively loops through a directory, changing an attribute on all of the underlying files.
366 /// An example is to add/remove the ReadOnly flag from each file.
367 /// </summary>
368 /// <param name="path">The directory path to start deleting from.</param>
369 /// <param name="fileAttribute">The FileAttribute to change on each file.</param>
370 /// <param name="messageHandler">The message handler.</param>
371 /// <param name="markAttribute">If true, add the attribute to each file. If false, remove it.</param>
372 private static void RecursiveFileAttributes(string path, FileAttributes fileAttribute, bool markAttribute, IMessaging messageHandler)
373 {
374 foreach (var subDirectory in Directory.GetDirectories(path))
375 {
376 RecursiveFileAttributes(subDirectory, fileAttribute, markAttribute, messageHandler);
377 }
378
379 foreach (var filePath in Directory.GetFiles(path))
380 {
381 var attributes = File.GetAttributes(filePath);
382 if (markAttribute)
383 {
384 attributes = attributes | fileAttribute; // add to list of attributes
385 }
386 else if (fileAttribute == (attributes & fileAttribute)) // if attribute set
387 {
388 attributes = attributes ^ fileAttribute; // remove from list of attributes
389 }
390
391 try
392 {
393 File.SetAttributes(filePath, attributes);
394 }
395 catch (UnauthorizedAccessException)
396 {
397 messageHandler.Write(WarningMessages.AccessDeniedForSettingAttributes(null, filePath));
398 }
399 }
400 }
401
402 /// <summary>
403 /// Takes an id, and demodularizes it (if possible). 365 /// Takes an id, and demodularizes it (if possible).
404 /// </summary> 366 /// </summary>
405 /// <remarks> 367 /// <remarks>
diff --git a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
index 4269cabe..8cf7a872 100644
--- a/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
+++ b/src/wix/WixToolset.Core/ExtensibilityServices/FileSystem.cs
@@ -3,6 +3,7 @@
3namespace WixToolset.Core.ExtensibilityServices 3namespace WixToolset.Core.ExtensibilityServices
4{ 4{
5 using System; 5 using System;
6 using System.ComponentModel;
6 using System.IO; 7 using System.IO;
7 using System.Runtime.InteropServices; 8 using System.Runtime.InteropServices;
8 using System.Threading; 9 using System.Threading;
@@ -50,6 +51,25 @@ namespace WixToolset.Core.ExtensibilityServices
50 this.ExecuteWithRetries(() => File.Move(source, destination)); 51 this.ExecuteWithRetries(() => File.Move(source, destination));
51 } 52 }
52 53
54 public FileStream OpenFile(string path, FileMode mode, FileAccess access, FileShare share)
55 {
56 const int maxRetries = 4;
57
58 for (var attempt = 1; attempt <= maxRetries; ++attempt)
59 {
60 try
61 {
62 return File.Open(path, mode, access, share);
63 }
64 catch (Exception e) when (attempt < maxRetries && (e is IOException || e is SystemException || e is Win32Exception))
65 {
66 Thread.Sleep(250);
67 }
68 }
69
70 throw new InvalidOperationException("Cannot reach this code");
71 }
72
53 public void ExecuteWithRetries(Action action, int maxRetries = 4) 73 public void ExecuteWithRetries(Action action, int maxRetries = 4)
54 { 74 {
55 for (var attempt = 1; attempt <= maxRetries; ++attempt) 75 for (var attempt = 1; attempt <= maxRetries; ++attempt)
@@ -59,7 +79,7 @@ namespace WixToolset.Core.ExtensibilityServices
59 action(); 79 action();
60 break; 80 break;
61 } 81 }
62 catch when (attempt < maxRetries) 82 catch (Exception e) when (attempt < maxRetries && (e is IOException || e is SystemException || e is Win32Exception))
63 { 83 {
64 Thread.Sleep(250); 84 Thread.Sleep(250);
65 } 85 }
diff --git a/src/wix/WixToolset.Core/LayoutCreator.cs b/src/wix/WixToolset.Core/LayoutCreator.cs
index 3e7ecf3a..c361f78a 100644
--- a/src/wix/WixToolset.Core/LayoutCreator.cs
+++ b/src/wix/WixToolset.Core/LayoutCreator.cs
@@ -112,15 +112,9 @@ namespace WixToolset.Core
112 // Clean up temp files. 112 // Clean up temp files.
113 foreach (var tempPath in uniqueTempPaths) 113 foreach (var tempPath in uniqueTempPaths)
114 { 114 {
115 try 115 this.SplitUniqueFolders(intermediateFolder, tempPath, uniqueFolders);
116 {
117 this.SplitUniqueFolders(intermediateFolder, tempPath, uniqueFolders);
118 116
119 File.Delete(tempPath); 117 this.FileSystem.DeleteFile(tempPath);
120 }
121 catch // delete is best effort.
122 {
123 }
124 } 118 }
125 119
126 // Clean up empty temp folders. 120 // Clean up empty temp folders.
@@ -140,7 +134,7 @@ namespace WixToolset.Core
140 { 134 {
141 if (tempPath.StartsWith(intermediateFolder, StringComparison.OrdinalIgnoreCase)) 135 if (tempPath.StartsWith(intermediateFolder, StringComparison.OrdinalIgnoreCase))
142 { 136 {
143 var folder = Path.GetDirectoryName(tempPath).Substring(intermediateFolder.Length); 137 var folder = Path.GetDirectoryName(tempPath.Substring(intermediateFolder.Length));
144 138
145 var parts = folder.Split(new[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries); 139 var parts = folder.Split(new[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries);
146 140