From add525424e44a050d3f98f64e56a85ab5d79955d Mon Sep 17 00:00:00 2001
From: Bob Arnson <bob@firegiant.com>
Date: Thu, 13 Feb 2020 14:28:58 -0500
Subject: Make BindResult disposable to manage WixOutput disposability.

---
 .../Bind/BindDatabaseCommand.cs                    | 55 ++++-----------
 src/WixToolset.Core.WindowsInstaller/MsiBackend.cs | 18 ++---
 src/WixToolset.Core.WindowsInstaller/MsmBackend.cs | 17 +++--
 src/WixToolset.Core.WindowsInstaller/MspBackend.cs | 17 +++--
 src/WixToolset.Core/BindResult.cs                  | 31 +++++++++
 src/WixToolset.Core/CommandLine/BuildCommand.cs    | 81 ++++++++++++----------
 6 files changed, 120 insertions(+), 99 deletions(-)

diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
index 1bcaf209..a90517da 100644
--- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs
@@ -17,13 +17,11 @@ namespace WixToolset.Core.WindowsInstaller.Bind
     /// <summary>
     /// Binds a databse.
     /// </summary>
-    internal class BindDatabaseCommand : IDisposable
+    internal class BindDatabaseCommand
     {
         // As outlined in RFC 4122, this is our namespace for generating name-based (version 3) UUIDs.
         internal static readonly Guid WixComponentGuidNamespace = new Guid("{3064E5C6-FB63-4FE9-AC49-E446A792EFA5}");
 
-        private bool disposed;
-
         public BindDatabaseCommand(IBindContext context, IEnumerable<IWindowsInstallerBackendBinderExtension> backendExtension, Validator validator):this(context, backendExtension, null, validator)
         {
         }
@@ -97,13 +95,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
 
         private Validator Validator { get; }
 
-        public IEnumerable<IFileTransfer> FileTransfers { get; private set; }
-
-        public IEnumerable<ITrackedFile> TrackedFiles { get; private set; }
-
-        public WixOutput Wixout { get; private set; }
-
-        public void Execute()
+        public IBindResult Execute()
         {
             var section = this.Intermediate.Sections.Single();
 
@@ -218,7 +210,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
 
             if (this.Messaging.EncounteredError)
             {
-                return;
+                return null;
             }
 
             // Call extension
@@ -290,7 +282,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // stop processing if an error previously occurred
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Gather information about files that do not come from merge modules.
@@ -322,7 +314,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // stop processing if an error previously occurred
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Now that the variable cache is populated, resolve any delayed fields.
@@ -347,7 +339,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // stop processing if an error previously occurred
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Time to create the output object. Try to put as much above here as possible, updating the IR is better.
@@ -425,7 +417,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // Stop processing if an error previously occurred.
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Ensure the intermediate folder is created since delta patches will be
@@ -479,7 +471,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // stop processing if an error previously occurred
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Generate database file.
@@ -496,7 +488,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 // Stop processing if an error previously occurred.
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
                 // Merge modules.
@@ -533,7 +525,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
 
                 if (this.Messaging.EncounteredError)
                 {
-                    return;
+                    return null;
                 }
 
 #if TODO_FINISH_VALIDATION
@@ -580,10 +572,12 @@ namespace WixToolset.Core.WindowsInstaller.Bind
                 trackedFiles.AddRange(fileFacades.Select(f => this.BackendHelper.TrackFile(f.SourcePath, TrackedFileType.Input, f.SourceLineNumber)));
             }
 
-            this.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output);
+            var result = this.ServiceProvider.GetService<IBindResult>();
+            result.FileTransfers = fileTransfers;
+            result.TrackedFiles = trackedFiles;
+            result.Wixout = this.CreateWixout(trackedFiles, this.Intermediate, output);
 
-            this.FileTransfers = fileTransfers;
-            this.TrackedFiles = trackedFiles;
+            return result;
         }
 
         private WixOutput CreateWixout(List<ITrackedFile> trackedFiles, Intermediate intermediate, WindowsInstallerData output)
@@ -984,24 +978,5 @@ namespace WixToolset.Core.WindowsInstaller.Bind
 
             return command.GeneratedTemporaryFiles;
         }
-
-#region IDisposable Support
-
-        public void Dispose() => this.Dispose(true);
-
-        protected virtual void Dispose(bool disposing)
-        {
-            if (!this.disposed)
-            {
-                if (disposing)
-                {
-                    this.Wixout?.Dispose();
-                }
-
-                this.disposed = true;
-            }
-        }
-
-#endregion
     }
 }
diff --git a/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
index 86c0fbfe..778720b9 100644
--- a/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MsiBackend.cs
@@ -25,22 +25,24 @@ namespace WixToolset.Core.WindowsInstaller
 
             var validator = Validator.CreateFromContext(context, "darice.cub");
 
-            using (var command = new BindDatabaseCommand(context, backendExtensions, validator))
+            IBindResult result = null;
+            try
             {
-                command.Execute();
-
-                var result = context.ServiceProvider.GetService<IBindResult>();
-                result.FileTransfers = command.FileTransfers;
-                result.TrackedFiles = command.TrackedFiles;
-                result.Wixout = command.Wixout;
+                var command = new BindDatabaseCommand(context, backendExtensions, validator);
+                result = command.Execute();
 
                 foreach (var extension in backendExtensions)
                 {
-                    extension.PostBackendBind(result, command.Wixout);
+                    extension.PostBackendBind(result);
                 }
 
                 return result;
             }
+            catch
+            {
+                result?.Dispose();
+                throw;
+            }
         }
 
         public IDecompileResult Decompile(IDecompileContext context)
diff --git a/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs b/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
index f048b4e2..4860bf81 100644
--- a/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MsmBackend.cs
@@ -24,21 +24,24 @@ namespace WixToolset.Core.WindowsInstaller
 
             var validator = Validator.CreateFromContext(context, "mergemod.cub");
 
-            using (var command = new BindDatabaseCommand(context, backendExtensions, validator))
+            IBindResult result = null;
+            try
             {
-                command.Execute();
-
-                var result = context.ServiceProvider.GetService<IBindResult>();
-                result.FileTransfers = command.FileTransfers;
-                result.TrackedFiles = command.TrackedFiles;
+                var command = new BindDatabaseCommand(context, backendExtensions, validator);
+                result = command.Execute();
 
                 foreach (var extension in backendExtensions)
                 {
-                    extension.PostBackendBind(result, command.Wixout);
+                    extension.PostBackendBind(result);
                 }
 
                 return result;
             }
+            catch
+            {
+                result?.Dispose();
+                throw;
+            }
         }
 
         public IDecompileResult Decompile(IDecompileContext context)
diff --git a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
index 8aa450bf..5dc64445 100644
--- a/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
+++ b/src/WixToolset.Core.WindowsInstaller/MspBackend.cs
@@ -47,21 +47,24 @@ namespace WixToolset.Core.WindowsInstaller
 
             // Create WindowsInstallerData with patch metdata and transforms as sub-storages
             // Create MSP from WindowsInstallerData
-            using (var command = new BindDatabaseCommand(context, backendExtensions, subStorages, null))
+            IBindResult result = null;
+            try
             {
-                command.Execute();
-
-                var result = context.ServiceProvider.GetService<IBindResult>();
-                result.FileTransfers = command.FileTransfers;
-                result.TrackedFiles = command.TrackedFiles;
+                var command = new BindDatabaseCommand(context, backendExtensions, subStorages, null);
+                result = command.Execute();
 
                 foreach (var extension in backendExtensions)
                 {
-                    extension.PostBackendBind(result, command.Wixout);
+                    extension.PostBackendBind(result);
                 }
 
                 return result;
             }
+            catch
+            {
+                result?.Dispose();
+                throw;
+            }
         }
 
         public IDecompileResult Decompile(IDecompileContext context) => throw new NotImplementedException();
diff --git a/src/WixToolset.Core/BindResult.cs b/src/WixToolset.Core/BindResult.cs
index c711b540..4edade7a 100644
--- a/src/WixToolset.Core/BindResult.cs
+++ b/src/WixToolset.Core/BindResult.cs
@@ -2,16 +2,47 @@
 
 namespace WixToolset.Core
 {
+    using System;
     using System.Collections.Generic;
     using WixToolset.Data;
     using WixToolset.Extensibility.Data;
 
     internal class BindResult : IBindResult
     {
+        private bool disposed;
+
         public IEnumerable<IFileTransfer> FileTransfers { get; set; }
 
         public IEnumerable<ITrackedFile> TrackedFiles { get; set; }
 
         public WixOutput Wixout { get; set; }
+
+        #region IDisposable Support
+        /// <summary>
+        /// Disposes of the internal state of the file structure.
+        /// </summary>
+        public void Dispose()
+        {
+            this.Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Disposes of the internsl state of the file structure.
+        /// </summary>
+        /// <param name="disposing">True if disposing.</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!this.disposed)
+            {
+                if (disposing)
+                {
+                    this.Wixout?.Dispose();
+                }
+            }
+
+            this.disposed = true;
+        }
+        #endregion
     }
 }
diff --git a/src/WixToolset.Core/CommandLine/BuildCommand.cs b/src/WixToolset.Core/CommandLine/BuildCommand.cs
index 0868a3b6..a55fb55b 100644
--- a/src/WixToolset.Core/CommandLine/BuildCommand.cs
+++ b/src/WixToolset.Core/CommandLine/BuildCommand.cs
@@ -294,47 +294,54 @@ namespace WixToolset.Core.CommandLine
                 return;
             }
 
-            IBindResult bindResult;
+            IBindResult bindResult = null;
+            try
             {
-                var context = this.ServiceProvider.GetService<IBindContext>();
-                //context.CabbingThreadCount = this.CabbingThreadCount;
-                context.BurnStubPath = burnStubPath;
-                context.CabCachePath = cabCachePath;
-                context.Codepage = resolveResult.Codepage;
-                //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
-                context.DelayedFields = resolveResult.DelayedFields;
-                context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
-                context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>();
-                context.Ices = Array.Empty<string>(); // TODO: set this correctly
-                context.IntermediateFolder = intermediateFolder;
-                context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
-                context.OutputPath = this.OutputFile;
-                context.OutputPdbPath = Path.ChangeExtension(this.OutputFile, ".wixpdb");
-                context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
-                context.SuppressValidation = true; // TODO: set this correctly
-
-                var binder = this.ServiceProvider.GetService<IBinder>();
-                bindResult = binder.Bind(context);
-            }
+                {
+                    var context = this.ServiceProvider.GetService<IBindContext>();
+                    //context.CabbingThreadCount = this.CabbingThreadCount;
+                    context.BurnStubPath = burnStubPath;
+                    context.CabCachePath = cabCachePath;
+                    context.Codepage = resolveResult.Codepage;
+                    //context.DefaultCompressionLevel = this.DefaultCompressionLevel;
+                    context.DelayedFields = resolveResult.DelayedFields;
+                    context.ExpectedEmbeddedFiles = resolveResult.ExpectedEmbeddedFiles;
+                    context.Extensions = this.ExtensionManager.GetServices<IBinderExtension>();
+                    context.Ices = Array.Empty<string>(); // TODO: set this correctly
+                    context.IntermediateFolder = intermediateFolder;
+                    context.IntermediateRepresentation = resolveResult.IntermediateRepresentation;
+                    context.OutputPath = this.OutputFile;
+                    context.OutputPdbPath = Path.ChangeExtension(this.OutputFile, ".wixpdb");
+                    context.SuppressIces = Array.Empty<string>(); // TODO: set this correctly
+                    context.SuppressValidation = true; // TODO: set this correctly
+
+                    var binder = this.ServiceProvider.GetService<IBinder>();
+                    bindResult = binder.Bind(context);
+                }
 
-            if (this.Messaging.EncounteredError)
-            {
-                return;
-            }
+                if (this.Messaging.EncounteredError)
+                {
+                    return;
+                }
 
+                {
+                    var context = this.ServiceProvider.GetService<ILayoutContext>();
+                    context.Extensions = this.ExtensionManager.GetServices<ILayoutExtension>();
+                    context.TrackedFiles = bindResult.TrackedFiles;
+                    context.FileTransfers = bindResult.FileTransfers;
+                    context.IntermediateFolder = intermediateFolder;
+                    context.ContentsFile = this.ContentsFile;
+                    context.OutputsFile = this.OutputsFile;
+                    context.BuiltOutputsFile = this.BuiltOutputsFile;
+                    context.SuppressAclReset = false; // TODO: correctly set SuppressAclReset
+
+                    var layout = this.ServiceProvider.GetService<ILayoutCreator>();
+                    layout.Layout(context);
+                }
+            }
+            finally
             {
-                var context = this.ServiceProvider.GetService<ILayoutContext>();
-                context.Extensions = this.ExtensionManager.GetServices<ILayoutExtension>();
-                context.TrackedFiles = bindResult.TrackedFiles;
-                context.FileTransfers = bindResult.FileTransfers;
-                context.IntermediateFolder = intermediateFolder;
-                context.ContentsFile = this.ContentsFile;
-                context.OutputsFile = this.OutputsFile;
-                context.BuiltOutputsFile = this.BuiltOutputsFile;
-                context.SuppressAclReset = false; // TODO: correctly set SuppressAclReset
-
-                var layout = this.ServiceProvider.GetService<ILayoutCreator>();
-                layout.Layout(context);
+                bindResult?.Dispose();
             }
         }
 
-- 
cgit v1.2.3-55-g6feb