diff --git a/src/api/burn/WixToolset.BootstrapperApplicationApi/IPackageInfo.cs b/src/api/burn/WixToolset.BootstrapperApplicationApi/IPackageInfo.cs
index e2512584a..9b5e74cb4 100644
--- a/src/api/burn/WixToolset.BootstrapperApplicationApi/IPackageInfo.cs
+++ b/src/api/burn/WixToolset.BootstrapperApplicationApi/IPackageInfo.cs
@@ -27,6 +27,11 @@ public interface IPackageInfo
///
string DisplayInternalUICondition { get; }
+ ///
+ /// The authored bal:DisplayFilesInUseDialogCondition.
+ ///
+ string DisplayFilesInUseDialogCondition { get; }
+
///
/// The package's display name.
///
diff --git a/src/api/burn/WixToolset.BootstrapperApplicationApi/PackageInfo.cs b/src/api/burn/WixToolset.BootstrapperApplicationApi/PackageInfo.cs
index e835f9ea2..81a8869f9 100644
--- a/src/api/burn/WixToolset.BootstrapperApplicationApi/PackageInfo.cs
+++ b/src/api/burn/WixToolset.BootstrapperApplicationApi/PackageInfo.cs
@@ -120,6 +120,9 @@ public class PackageInfo : IPackageInfo
///
public string DisplayInternalUICondition { get; internal set; }
+ ///
+ public string DisplayFilesInUseDialogCondition { get; internal set; }
+
///
public string ProductCode { get; internal set; }
@@ -363,6 +366,7 @@ internal static void ParseBalPackageInfoFromXml(XPathNavigator root, XmlNamespac
var package = (PackageInfo)ipackage;
package.DisplayInternalUICondition = BootstrapperApplicationData.GetAttribute(node, "DisplayInternalUICondition");
+ package.DisplayFilesInUseDialogCondition = BootstrapperApplicationData.GetAttribute(node, "DisplayFilesInUseDialogCondition");
}
nodes = root.Select("/p:BootstrapperApplicationData/p:WixPrereqInformation", namespaceManager);
diff --git a/src/api/burn/balutil/balinfo.cpp b/src/api/burn/balutil/balinfo.cpp
index 38c4bd186..ff0dfd9f1 100644
--- a/src/api/burn/balutil/balinfo.cpp
+++ b/src/api/burn/balutil/balinfo.cpp
@@ -291,6 +291,7 @@ DAPI_(void) BalInfoUninitialize(
ReleaseStr(pBundle->packages.rgPackages[i].sczDescription);
ReleaseStr(pBundle->packages.rgPackages[i].sczId);
ReleaseStr(pBundle->packages.rgPackages[i].sczDisplayInternalUICondition);
+ ReleaseStr(pBundle->packages.rgPackages[i].sczDisplayFilesInUseDialogCondition);
ReleaseStr(pBundle->packages.rgPackages[i].sczProductCode);
ReleaseStr(pBundle->packages.rgPackages[i].sczUpgradeCode);
ReleaseStr(pBundle->packages.rgPackages[i].sczVersion);
@@ -517,6 +518,9 @@ static HRESULT ParseBalPackageInfoFromXml(
hr = XmlGetAttributeEx(pNode, L"DisplayInternalUICondition", &pPackage->sczDisplayInternalUICondition);
ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get DisplayInternalUICondition setting for package.");
+ hr = XmlGetAttributeEx(pNode, L"DisplayFilesInUseDialogCondition", &pPackage->sczDisplayFilesInUseDialogCondition);
+ ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get DisplayFilesInUseDialogCondition setting for package.");
+
hr = XmlGetAttributeEx(pNode, L"PrimaryPackageType", &scz);
ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get PrimaryPackageType setting for package.");
diff --git a/src/api/burn/balutil/inc/balinfo.h b/src/api/burn/balutil/inc/balinfo.h
index 234284f6f..8baee8445 100644
--- a/src/api/burn/balutil/inc/balinfo.h
+++ b/src/api/burn/balutil/inc/balinfo.h
@@ -54,6 +54,7 @@ typedef struct _BAL_INFO_PACKAGE
BOOL fPermanent;
BOOL fVital;
LPWSTR sczDisplayInternalUICondition;
+ LPWSTR sczDisplayFilesInUseDialogCondition;
LPWSTR sczProductCode;
LPWSTR sczUpgradeCode;
LPWSTR sczVersion;
diff --git a/src/ext/Bal/stdbas/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/stdbas/WixStandardBootstrapperApplication.cpp
index 6cfe0b4cc..ddb2d1c53 100644
--- a/src/ext/Bal/stdbas/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/stdbas/WixStandardBootstrapperApplication.cpp
@@ -1231,34 +1231,52 @@ class CWixStandardBootstrapperApplication : public CBootstrapperApplicationBase
__inout int* pResult
)
{
+ HRESULT hr = S_OK;
+ BAL_INFO_PACKAGE* pPackage = NULL;
+ BOOL fShowFilesInUseDialog = TRUE;
if (!m_fShowingInternalUiThisPackage && wzPackageId && *wzPackageId)
{
BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Package %ls has %d applications holding files in use.", wzPackageId, cFiles);
- switch (source)
+ hr = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
+ if (SUCCEEDED(hr) && pPackage->sczDisplayFilesInUseDialogCondition)
{
- case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI:
- if (m_fShowStandardFilesInUse)
- {
- return ShowMsiFilesInUse(cFiles, rgwzFiles, source, pResult);
- }
- break;
- case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM:
- if (m_fShowRMFilesInUse)
- {
- return ShowMsiFilesInUse(cFiles, rgwzFiles, source, pResult);
- }
- break;
- case BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX:
- if (m_fShowNetfxFilesInUse)
+ hr = BalEvaluateCondition(pPackage->sczDisplayFilesInUseDialogCondition, &fShowFilesInUseDialog);
+ BalExitOnFailure(hr, "Failed to evaluate condition for package '%ls': %ls", wzPackageId, pPackage->sczDisplayFilesInUseDialogCondition);
+ }
+
+ if (fShowFilesInUseDialog)
+ {
+ switch (source)
{
- return ShowNetfxFilesInUse(cFiles, rgwzFiles, pResult);
+ case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI:
+ if (m_fShowStandardFilesInUse)
+ {
+ return ShowMsiFilesInUse(cFiles, rgwzFiles, source, pResult);
+ }
+ break;
+ case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM:
+ if (m_fShowRMFilesInUse)
+ {
+ return ShowMsiFilesInUse(cFiles, rgwzFiles, source, pResult);
+ }
+ break;
+ case BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX:
+ if (m_fShowNetfxFilesInUse)
+ {
+ return ShowNetfxFilesInUse(cFiles, rgwzFiles, pResult);
+ }
+ break;
}
- break;
+ }
+ else
+ {
+ *pResult = IDIGNORE;
}
}
+ LExit:
return __super::OnExecuteFilesInUse(wzPackageId, cFiles, rgwzFiles, nRecommendation, source, pResult);
}
diff --git a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/BalExtensionFixture.cs b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/BalExtensionFixture.cs
index 07d3dacbd..576e7c6ce 100644
--- a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/BalExtensionFixture.cs
+++ b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/BalExtensionFixture.cs
@@ -55,6 +55,44 @@ public void CanBuildUsingDisplayInternalUICondition()
}
}
+ [TestMethod]
+ public void CanBuildUsingDisplayFilesInUseDialogCondition()
+ {
+ using (var fs = new DisposableFileSystem())
+ {
+ var baseFolder = fs.GetFolder();
+ var bundleFile = Path.Combine(baseFolder, "bin", "test.exe");
+ var bundleSourceFolder = TestData.Get(@"TestData\WixStdBa");
+ var intermediateFolder = Path.Combine(baseFolder, "obj");
+ var baFolderPath = Path.Combine(baseFolder, "ba");
+ var extractFolderPath = Path.Combine(baseFolder, "extract");
+
+ var compileResult = WixRunner.Execute(new[]
+ {
+ "build",
+ Path.Combine(bundleSourceFolder, "DisplayFilesInUseDialogConditionBundle.wxs"),
+ "-ext", TestData.Get(@"WixToolset.BootstrapperApplications.wixext.dll"),
+ "-intermediateFolder", intermediateFolder,
+ "-bindpath", Path.Combine(bundleSourceFolder, "data"),
+ "-o", bundleFile,
+ });
+ compileResult.AssertSuccess();
+
+ Assert.IsTrue(File.Exists(bundleFile));
+
+ var extractResult = BundleExtractor.ExtractBAContainer(null, bundleFile, baFolderPath, extractFolderPath);
+ extractResult.AssertSuccess();
+
+ var balPackageInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixBalPackageInfo");
+ WixAssert.CompareLineByLine(new string[]
+ {
+ "",
+ }, balPackageInfos);
+
+ Assert.IsTrue(File.Exists(Path.Combine(baFolderPath, "thm.wxl")));
+ }
+ }
+
[TestMethod]
public void CanBuildUsingBootstrapperApplicationId()
{
@@ -298,6 +336,7 @@ public void CannotBuildUsingOverridableWrongCase()
{
"bal:Condition/@Condition contains the built-in Variable 'WixBundleAction', which is not available when it is evaluated. (Unavailable Variables are: 'WixBundleAction'.). Rewrite the condition to avoid Variables that are never valid during its evaluation.",
"Overridable variable 'TEST1' collides with 'Test1' with Bundle/@CommandLineVariables value 'caseInsensitive'.",
+ "The *Package/@bal:DisplayFilesInUseDialogCondition attribute's value '=' is not a valid bundle condition.",
"The *Package/@bal:DisplayInternalUICondition attribute's value '=' is not a valid bundle condition.",
"The location of the Variable related to the previous error.",
}, messages.ToArray());
diff --git a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/InternalUIBAFixture.cs b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/InternalUIBAFixture.cs
index 72e315406..7b4d00fc4 100644
--- a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/InternalUIBAFixture.cs
+++ b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/InternalUIBAFixture.cs
@@ -168,6 +168,7 @@ public void CanBuildUsingWixIuiBaWithWarnings()
"WixInternalUIBootstrapperApplication does not support the value of 'force' for Cache on prereq packages. Prereq packages are only cached when they need to be installed.",
"WixInternalUIBootstrapperApplication ignores InstallCondition for the primary package so that the MSI UI is always shown.",
"WixInternalUIBootstrapperApplication ignores DisplayInternalUICondition for the primary package so that the MSI UI is always shown.",
+ "WixInternalUIBootstrapperApplication ignores DisplayFilesInUseDialogCondition for the primary package so that the MSI UI is always shown.",
"When using WixInternalUIBootstrapperApplication, all prereq packages should be before the primary package in the chain. The prereq packages are always installed before the primary package.",
}, compileResult.Messages.Select(m => m.ToString()).ToArray());
@@ -181,7 +182,7 @@ public void CanBuildUsingWixIuiBaWithWarnings()
var balPackageInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixBalPackageInfo");
WixAssert.CompareLineByLine(new string[]
{
- "",
+ "",
}, balPackageInfos);
var mbaPrereqInfos = extractResult.GetBADataTestXmlLines("/ba:BootstrapperApplicationData/ba:WixPrereqInformation");
diff --git a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/Overridable/WrongCaseBundle.wxs b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/Overridable/WrongCaseBundle.wxs
index 67dfc5894..33b2d64cf 100644
--- a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/Overridable/WrongCaseBundle.wxs
+++ b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/Overridable/WrongCaseBundle.wxs
@@ -9,7 +9,7 @@
-
+
diff --git a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixIuiBa/IuibaWarnings.wxs b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixIuiBa/IuibaWarnings.wxs
index 2cf9787df..9c9aa0f87 100644
--- a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixIuiBa/IuibaWarnings.wxs
+++ b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixIuiBa/IuibaWarnings.wxs
@@ -6,7 +6,7 @@
-
+
diff --git a/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixStdBa/DisplayFilesInUseDialogConditionBundle.wxs b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixStdBa/DisplayFilesInUseDialogConditionBundle.wxs
new file mode 100644
index 000000000..1041eb39a
--- /dev/null
+++ b/src/ext/Bal/test/WixToolsetTest.BootstrapperApplications/TestData/WixStdBa/DisplayFilesInUseDialogConditionBundle.wxs
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/ext/Bal/wixext/BalBurnBackendExtension.cs b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
index 821955496..5831bb8a4 100644
--- a/src/ext/Bal/wixext/BalBurnBackendExtension.cs
+++ b/src/ext/Bal/wixext/BalBurnBackendExtension.cs
@@ -51,6 +51,11 @@ public override bool TryProcessSymbol(IntermediateSection section, IntermediateS
writer.WriteAttributeString("DisplayInternalUICondition", balPackageInfoSymbol.DisplayInternalUICondition);
}
+ if (balPackageInfoSymbol.DisplayFilesInUseDialogCondition != null)
+ {
+ writer.WriteAttributeString("DisplayFilesInUseDialogCondition", balPackageInfoSymbol.DisplayFilesInUseDialogCondition);
+ }
+
if (balPackageInfoSymbol.PrimaryPackageType != BalPrimaryPackageType.None)
{
writer.WriteAttributeString("PrimaryPackageType", balPackageInfoSymbol.PrimaryPackageType.ToString().ToLower());
@@ -104,6 +109,7 @@ public override void SymbolsFinalized(IntermediateSection section)
this.VerifyBalConditions(section);
this.VerifyDisplayInternalUICondition(section);
+ this.VerifyDisplayFilesInUseDialogCondition(section);
this.VerifyOverridableVariables(section);
var balBaSymbol = section.Symbols.OfType().SingleOrDefault();
@@ -195,6 +201,17 @@ private void VerifyDisplayInternalUICondition(IntermediateSection section)
}
}
+ private void VerifyDisplayFilesInUseDialogCondition(IntermediateSection section)
+ {
+ foreach (var balPackageInfoSymbol in section.Symbols.OfType().ToList())
+ {
+ if (balPackageInfoSymbol.DisplayFilesInUseDialogCondition != null)
+ {
+ this.BackendHelper.ValidateBundleCondition(balPackageInfoSymbol.SourceLineNumbers, "*Package", "bal:DisplayFilesInUseDialogCondition", balPackageInfoSymbol.DisplayFilesInUseDialogCondition, BundleConditionPhase.Plan);
+ }
+ }
+ }
+
private void VerifyPrimaryPackages(IntermediateSection section, SourceLineNumber baSourceLineNumbers)
{
WixBalPackageInfoSymbol defaultPrimaryPackage = null;
@@ -420,6 +437,11 @@ private void VerifyIuibaPrimaryPackage(WixBundlePackageSymbol packageSymbol, Wix
{
this.Messaging.Write(BalWarnings.IuibaPrimaryPackageDisplayInternalUICondition(packageSymbol.SourceLineNumbers));
}
+
+ if (balPackageInfoSymbol.DisplayFilesInUseDialogCondition != null)
+ {
+ this.Messaging.Write(BalWarnings.IuibaPrimaryPackageDisplayFilesInUseDialogCondition(packageSymbol.SourceLineNumbers));
+ }
}
private void VerifyOverridableVariables(IntermediateSection section)
diff --git a/src/ext/Bal/wixext/BalCompiler.cs b/src/ext/Bal/wixext/BalCompiler.cs
index 35c86233c..b7d5f679f 100644
--- a/src/ext/Bal/wixext/BalCompiler.cs
+++ b/src/ext/Bal/wixext/BalCompiler.cs
@@ -200,6 +200,20 @@ public override void ParseAttribute(Intermediate intermediate, IntermediateSecti
break;
}
break;
+ case "DisplayFilesInUseDialogCondition":
+ switch (parentElement.Name.LocalName)
+ {
+ case "MsiPackage":
+ case "MspPackage":
+ var displayFilesInUseDialogCondition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attribute);
+ var packageInfo = this.GetBalPackageInfoSymbol(section, sourceLineNumbers, packageId);
+ packageInfo.DisplayFilesInUseDialogCondition = displayFilesInUseDialogCondition;
+ break;
+ default:
+ this.ParseHelper.UnexpectedAttribute(parentElement, attribute);
+ break;
+ }
+ break;
case "PrimaryPackageType":
{
var primaryPackageType = BalPrimaryPackageType.None;
diff --git a/src/ext/Bal/wixext/BalWarnings.cs b/src/ext/Bal/wixext/BalWarnings.cs
index 8c5d892f1..f86837f9f 100644
--- a/src/ext/Bal/wixext/BalWarnings.cs
+++ b/src/ext/Bal/wixext/BalWarnings.cs
@@ -23,6 +23,11 @@ public static Message IuibaPrimaryPackageDisplayInternalUICondition(SourceLineNu
return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageDisplayInternalUICondition, "WixInternalUIBootstrapperApplication ignores DisplayInternalUICondition for the primary package so that the MSI UI is always shown.");
}
+ public static Message IuibaPrimaryPackageDisplayFilesInUseDialogCondition(SourceLineNumber sourceLineNumbers)
+ {
+ return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageDisplayFilesInUseDialogCondition, "WixInternalUIBootstrapperApplication ignores DisplayFilesInUseDialogCondition for the primary package so that the MSI UI is always shown.");
+ }
+
public static Message IuibaPrimaryPackageInstallCondition(SourceLineNumber sourceLineNumbers)
{
return Message(sourceLineNumbers, Ids.IuibaPrimaryPackageInstallCondition, "WixInternalUIBootstrapperApplication ignores InstallCondition for the primary package so that the MSI UI is always shown.");
@@ -56,6 +61,7 @@ public enum Ids
IuibaPrimaryPackageDisplayInternalUICondition = 6504,
IuibaPrereqPackageAfterPrimaryPackage = 6505,
DeprecatedBAFactoryAssemblyAttribute = 6506,
+ IuibaPrimaryPackageDisplayFilesInUseDialogCondition = 6507,
}
}
}
diff --git a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
index e2636d33e..6e90c2b90 100644
--- a/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
+++ b/src/ext/Bal/wixext/Symbols/WixBalPackageInfoSymbol.cs
@@ -14,6 +14,7 @@ public static partial class BalSymbolDefinitions
new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PackageId), IntermediateFieldType.String),
new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayInternalUICondition), IntermediateFieldType.String),
new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.PrimaryPackageType), IntermediateFieldType.Number),
+ new IntermediateFieldDefinition(nameof(WixBalPackageInfoSymbolFields.DisplayFilesInUseDialogCondition), IntermediateFieldType.String),
},
typeof(WixBalPackageInfoSymbol));
}
@@ -28,6 +29,7 @@ public enum WixBalPackageInfoSymbolFields
PackageId,
DisplayInternalUICondition,
PrimaryPackageType,
+ DisplayFilesInUseDialogCondition,
}
public enum BalPrimaryPackageType
@@ -68,5 +70,11 @@ public BalPrimaryPackageType PrimaryPackageType
get => (BalPrimaryPackageType)this.Fields[(int)WixBalPackageInfoSymbolFields.PrimaryPackageType].AsNumber();
set => this.Set((int)WixBalPackageInfoSymbolFields.PrimaryPackageType, (int)value);
}
+
+ public string DisplayFilesInUseDialogCondition
+ {
+ get => this.Fields[(int)WixBalPackageInfoSymbolFields.DisplayFilesInUseDialogCondition].AsString();
+ set => this.Set((int)WixBalPackageInfoSymbolFields.DisplayFilesInUseDialogCondition, value);
+ }
}
}
diff --git a/src/xsd/bal.xsd b/src/xsd/bal.xsd
index ff4142ad3..59c1f91e5 100644
--- a/src/xsd/bal.xsd
+++ b/src/xsd/bal.xsd
@@ -623,6 +623,21 @@
+
+
+
+ Specifies whether the bundle allows the Files In Use dialog to be displayed for the MSI or MSP package. If not
+ specified (the default) or the condition evaluates to true, the Files In Use dialog prompts the user during full
+ bundle UI to close applications to release the file. Otherwise, the dialog is not displayed and the package might
+ require a restart when files are in use.
+
+
+
+
+
+
+
+