diff --git a/BalExtensionExt.sln b/BalExtensionExt.sln index 0031fd9..e7bcfae 100644 --- a/BalExtensionExt.sln +++ b/BalExtensionExt.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wixextba", "wixstdba\wixstdba.vcxproj", "{41085A22-E6AA-4E8B-AB1B-DDEE0DC89DFA}" EndProject Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "BalExtension", "wixlib\BalExtension.wixproj", "{3444D952-F21C-496F-AB6B-56435BFD0787}" diff --git a/Examples/Bundle1.wxs b/Examples/Bundle1.wxs index 3e64582..c1bb7bc 100644 --- a/Examples/Bundle1.wxs +++ b/Examples/Bundle1.wxs @@ -2,7 +2,7 @@ - diff --git a/Examples/Bundle10.wxs b/Examples/Bundle10.wxs index 1eba28a..60e4cd9 100644 --- a/Examples/Bundle10.wxs +++ b/Examples/Bundle10.wxs @@ -4,14 +4,14 @@ It also shows how to use the custom action extension. --> - - + diff --git a/Examples/Bundle11.wxs b/Examples/Bundle11.wxs index ea8b67d..f5c4403 100644 --- a/Examples/Bundle11.wxs +++ b/Examples/Bundle11.wxs @@ -1,11 +1,11 @@ - @@ -13,7 +13,7 @@ Manufacturer="ACME" SplashScreenSourceFile="Resource\SplashScreen.bmp" AboutUrl="http://wixtoolset.org"> - + diff --git a/Examples/Bundle2.wxs b/Examples/Bundle2.wxs index d5ec53c..c3180a7 100644 --- a/Examples/Bundle2.wxs +++ b/Examples/Bundle2.wxs @@ -5,7 +5,7 @@ It also shows the use of two folder selections and radio buttons on the options page. --> - @@ -13,7 +13,10 @@ Manufacturer="ACME" AboutUrl="http://wixtoolset.org"> - + + + + diff --git a/Examples/Bundle3.wxs b/Examples/Bundle3.wxs index f3d862f..febf860 100644 --- a/Examples/Bundle3.wxs +++ b/Examples/Bundle3.wxs @@ -5,7 +5,7 @@ It also shows the use of radio buttons install page and a textbox on the options page. --> - diff --git a/Examples/Bundle4.wxs b/Examples/Bundle4.wxs index 2ccf287..5ea87ae 100644 --- a/Examples/Bundle4.wxs +++ b/Examples/Bundle4.wxs @@ -5,7 +5,7 @@ It also shows the use of checkboxes on the install and option pages. --> - diff --git a/Examples/Bundle5.wxs b/Examples/Bundle5.wxs index fb5c776..96b96a8 100644 --- a/Examples/Bundle5.wxs +++ b/Examples/Bundle5.wxs @@ -4,7 +4,7 @@ It also shows how to replace the standard logo with your own. --> - diff --git a/Examples/Bundle6.wxs b/Examples/Bundle6.wxs index a0b0582..04587bd 100644 --- a/Examples/Bundle6.wxs +++ b/Examples/Bundle6.wxs @@ -2,7 +2,7 @@ - diff --git a/Examples/Bundle7.wxs b/Examples/Bundle7.wxs index d9cf7f4..2318d96 100644 --- a/Examples/Bundle7.wxs +++ b/Examples/Bundle7.wxs @@ -4,7 +4,7 @@ It also shows how to disable the options page and license link. --> - diff --git a/Examples/Bundle8.wxs b/Examples/Bundle8.wxs index e262a3b..8924e77 100644 --- a/Examples/Bundle8.wxs +++ b/Examples/Bundle8.wxs @@ -4,7 +4,7 @@ It also shows how to override the default logo. --> - diff --git a/Examples/Bundle9.wxs b/Examples/Bundle9.wxs index e939f38..201fb56 100644 --- a/Examples/Bundle9.wxs +++ b/Examples/Bundle9.wxs @@ -4,7 +4,7 @@ It also shows how to use the custom action extension. --> - diff --git a/Examples/Resource/1031/HyperlinkTheme.wxl b/Examples/Resource/1031/HyperlinkTheme.wxl index fd66dfa..85d43dd 100644 --- a/Examples/Resource/1031/HyperlinkTheme.wxl +++ b/Examples/Resource/1031/HyperlinkTheme.wxl @@ -6,6 +6,7 @@ Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. Version [WixBundleVersion] Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -36,14 +37,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel \ No newline at end of file diff --git a/Examples/Resource/2057/HyperlinkTheme.wxl b/Examples/Resource/2057/HyperlinkTheme.wxl index 704cad1..d25713e 100644 --- a/Examples/Resource/2057/HyperlinkTheme.wxl +++ b/Examples/Resource/2057/HyperlinkTheme.wxl @@ -6,6 +6,7 @@ Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. Version [WixBundleVersion] Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -36,14 +37,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel \ No newline at end of file diff --git a/Examples/Resource/Bundle2Theme.xml b/Examples/Resource/Bundle2Theme.xml index 8788f2b..84bb71b 100644 --- a/Examples/Resource/Bundle2Theme.xml +++ b/Examples/Resource/Bundle2Theme.xml @@ -50,6 +50,17 @@ + + #(loc.Title) + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + #(loc.Title) @@ -70,7 +81,10 @@ #(loc.Title) - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) #(loc.SuccessRestartText) @@ -79,7 +93,10 @@ #(loc.Title) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) #(loc.FailureRestartText) diff --git a/Examples/Resource/Bundle3Theme.xml b/Examples/Resource/Bundle3Theme.xml index a0ba158..a32bcbe 100644 --- a/Examples/Resource/Bundle3Theme.xml +++ b/Examples/Resource/Bundle3Theme.xml @@ -48,6 +48,17 @@ + + #(loc.Title) + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + #(loc.Title) @@ -68,7 +79,10 @@ #(loc.Title) - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) #(loc.SuccessRestartText) @@ -77,7 +91,10 @@ #(loc.Title) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) #(loc.FailureRestartText) diff --git a/Examples/Resource/Bundle4Theme.xml b/Examples/Resource/Bundle4Theme.xml index 517d7ff..c4b5ed3 100644 --- a/Examples/Resource/Bundle4Theme.xml +++ b/Examples/Resource/Bundle4Theme.xml @@ -49,6 +49,17 @@ + + #(loc.Title) + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + #(loc.Title) @@ -69,7 +80,10 @@ #(loc.Title) - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) #(loc.SuccessRestartText) @@ -78,7 +92,10 @@ #(loc.Title) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) #(loc.FailureRestartText) diff --git a/Examples/Resource/HyperlinkLocaleTheme.wxl b/Examples/Resource/HyperlinkLocaleTheme.wxl index 5bfb902..14729c6 100644 --- a/Examples/Resource/HyperlinkLocaleTheme.wxl +++ b/Examples/Resource/HyperlinkLocaleTheme.wxl @@ -6,6 +6,7 @@ Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. Version [WixBundleVersion] Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -36,14 +37,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel \ No newline at end of file diff --git a/Examples/Resource/HyperlinkTheme.wxl b/Examples/Resource/HyperlinkTheme.wxl index 955ba61..b59bdcb 100644 --- a/Examples/Resource/HyperlinkTheme.wxl +++ b/Examples/Resource/HyperlinkTheme.wxl @@ -6,6 +6,7 @@ Setup will install [WixBundleName] on your computer. Click install to continue, options to set the install directory or Close to exit. Version [WixBundleVersion] Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -36,14 +37,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel \ No newline at end of file diff --git a/Examples/bafunctions.dll b/Examples/bafunctions.dll index ae079bb..62cef6e 100644 Binary files a/Examples/bafunctions.dll and b/Examples/bafunctions.dll differ diff --git a/bafunctions/bafunctions.vcxproj b/bafunctions/bafunctions.vcxproj index 83d9607..0a63e97 100644 --- a/bafunctions/bafunctions.vcxproj +++ b/bafunctions/bafunctions.vcxproj @@ -62,7 +62,7 @@ Disabled - $(WiX)SDK\VS2010\inc;%(AdditionalIncludeDirectories) + $(WiX)SDK\VS2013\inc;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;DEBUG;%(PreprocessorDefinitions);$(ExtraDefinitions) true EnableFastChecks @@ -80,7 +80,7 @@ dutil.lib;balutil.lib;comctl32.lib;gdiplus.lib;shlwapi.lib;wininet.lib;version.lib;%(AdditionalDependencies) - $(WiX)SDK\VS2010\lib\x86;%(AdditionalLibraryDirectories) + $(WiX)SDK\VS2013\lib\x86;%(AdditionalLibraryDirectories) bafunctions.def true Windows @@ -89,7 +89,7 @@ Disabled - $(WiX)SDK\VS2010\inc;%(AdditionalIncludeDirectories) + $(WiX)SDK\VS2013\inc;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);$(ExtraDefinitions) true EnableFastChecks @@ -107,7 +107,7 @@ dutil.lib;balutil.lib;comctl32.lib;gdiplus.lib;shlwapi.lib;wininet.lib;version.lib;%(AdditionalDependencies) - $(WiX)SDK\VS2010\lib\x86;%(AdditionalLibraryDirectories) + $(WiX)SDK\VS2013\lib\x86;%(AdditionalLibraryDirectories) bafunctions.def false Windows diff --git a/bafunctions/bafunctionsver.h b/bafunctions/bafunctionsver.h index 7b0d107..955173e 100644 --- a/bafunctions/bafunctionsver.h +++ b/bafunctions/bafunctionsver.h @@ -3,10 +3,10 @@ #define _VERSION_FILE_H_ #define szVerMajorMinor "1.0" -#define szVerMajorMinorBuildRev "1.0.5260.23891" +#define szVerMajorMinorBuildRev "1.0.5758.27001" #define rmj 1 #define rmm 0 -#define rbd 5260 -#define rev 23891 +#define rbd 5758 +#define rev 27001 #endif diff --git a/build.bat b/build.bat index 90d8beb..3492749 100644 --- a/build.bat +++ b/build.bat @@ -12,7 +12,7 @@ Call :DeleteDir "ipch" %MSBUILD% inc\Version.proj %MSBUILD% bafunctions\Version.proj -%MSBUILD% BalExtensionExt.sln /t:Rebuild /p:Configuration=Release /p:Platform="Mixed Platforms" /p:RunCodeAnalysis=false /p:DefineConstants="TRACE" /p:OutDir="%outdir%\\" /l:FileLogger,Microsoft.Build.Engine;logfile=build.log +%MSBUILD% BalExtensionExt.sln /t:Rebuild /p:VisualStudioVersion=12.0 /p:Configuration=Release /p:Platform="Mixed Platforms" /p:RunCodeAnalysis=false /p:DefineConstants="TRACE" /p:OutDir="%outdir%\\" /l:FileLogger,Microsoft.Build.Engine;logfile=build.log if %errorlevel% neq 0 ( echo Build failed pause diff --git a/build/WixBalExtensionExt.dll b/build/WixBalExtensionExt.dll index 5ab6898..6f19270 100644 Binary files a/build/WixBalExtensionExt.dll and b/build/WixBalExtensionExt.dll differ diff --git a/build/balExt.wixlib b/build/balExt.wixlib index f7c45eb..6b0945f 100644 Binary files a/build/balExt.wixlib and b/build/balExt.wixlib differ diff --git a/inc/Version.proj b/inc/Version.proj index 42ab9c5..a37bed3 100644 --- a/inc/Version.proj +++ b/inc/Version.proj @@ -2,7 +2,7 @@ 3 - 8 + 10 .\wixver.h .\wixver.cs diff --git a/inc/wixver.cs b/inc/wixver.cs index e82ef78..d701ace 100644 --- a/inc/wixver.cs +++ b/inc/wixver.cs @@ -1,4 +1,4 @@ // using System.Reflection; [assembly:AssemblyVersion("3.0.0.0")] -[assembly:AssemblyFileVersion("3.8.5260.23891")] +[assembly:AssemblyFileVersion("3.10.5758.27000")] diff --git a/inc/wixver.h b/inc/wixver.h index ba6381f..1590067 100644 --- a/inc/wixver.h +++ b/inc/wixver.h @@ -2,12 +2,12 @@ #ifndef _VERSION_FILE_H_ #define _VERSION_FILE_H_ -#define szVerMajorMinor "3.8" -#define szVerMajorMinorBuildRev "3.8.5260.23891" +#define szVerMajorMinor "3.10" +#define szVerMajorMinorBuildRev "3.10.5758.27000" #define rmj 3 -#define rmm 8 -#define rbd 5260 -#define rev 23891 +#define rmm 10 +#define rbd 5758 +#define rev 27000 #define szVerName "BalExtensionExt Release" #endif diff --git a/wixext/BalCompiler.cs b/wixext/BalCompiler.cs index b2b77e5..087de90 100644 --- a/wixext/BalCompiler.cs +++ b/wixext/BalCompiler.cs @@ -100,6 +100,32 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions { switch (parentElement.LocalName) { + case "ExePackage": + case "MsiPackage": + case "MspPackage": + case "MsuPackage": + string packageId; + if (!contextValues.TryGetValue("PackageId", out packageId) || String.IsNullOrEmpty(packageId)) + { + this.Core.OnMessage(WixErrors.ExpectedAttribute(sourceLineNumbers, parentElement.LocalName, "Id", attribute.LocalName)); + } + else + { + switch (attribute.LocalName) + { + case "PrereqSupportPackage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attribute)) + { + Row row = this.Core.CreateRow(sourceLineNumbers, "MbaPrerequisiteSupportPackage"); + row[0] = packageId; + } + break; + default: + this.Core.UnexpectedAttribute(sourceLineNumbers, attribute); + break; + } + } + break; case "Variable": // at the time the extension attribute is parsed, the compiler might not yet have // parsed the Name attribute, so we need to get it directly from the parent element. @@ -208,6 +234,10 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions { SourceLineNumberCollection sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string launchTarget = null; + string launchTargetElevatedId = null; + string launchArguments = null; + YesNoType launchHidden = YesNoType.NotSet; + string launchWorkingDir = null; string licenseFile = null; string licenseUrl = null; string logoFile = null; @@ -218,6 +248,8 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions YesNoType suppressDowngradeFailure = YesNoType.NotSet; YesNoType suppressRepair = YesNoType.NotSet; YesNoType showVersion = YesNoType.NotSet; + YesNoType supportCacheOnly = YesNoType.NotSet; + YesNoType showFilesInUse = YesNoType.NotSet; YesNoType launchPassive = YesNoType.NotSet; YesNoType launchQuiet = YesNoType.NotSet; @@ -230,6 +262,18 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions case "LaunchTarget": launchTarget = this.Core.GetAttributeValue(sourceLineNumbers, attrib, false); break; + case "LaunchTargetElevatedId": + launchTargetElevatedId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "LaunchArguments": + launchArguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib, false); + break; + case "LaunchHidden": + launchHidden = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "LaunchWorkingFolder": + launchWorkingDir = this.Core.GetAttributeValue(sourceLineNumbers, attrib, false); + break; case "LicenseFile": licenseFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib, false); break; @@ -266,6 +310,12 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions case "ShowVersion": showVersion = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; + case "SupportCacheOnly": + supportCacheOnly = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "ShowFilesInUse": + showFilesInUse = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; default: this.Core.UnexpectedAttribute(sourceLineNumbers, attrib); break; @@ -304,6 +354,26 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions this.Core.CreateVariableRow(sourceLineNumbers, "LaunchTarget", launchTarget, "string", false, false); } + if (!String.IsNullOrEmpty(launchTargetElevatedId)) + { + this.Core.CreateVariableRow(sourceLineNumbers, "LaunchTargetElevatedId", launchTargetElevatedId, "string", false, false); + } + + if (!String.IsNullOrEmpty(launchArguments)) + { + this.Core.CreateVariableRow(sourceLineNumbers, "LaunchArguments", launchArguments, "string", false, false); + } + + if (YesNoType.Yes == launchHidden) + { + this.Core.CreateVariableRow(sourceLineNumbers, "LaunchHidden", "yes", "string", false, false); + } + + if (!String.IsNullOrEmpty(launchWorkingDir)) + { + this.Core.CreateVariableRow(sourceLineNumbers, "LaunchWorkingFolder", launchWorkingDir, "string", false, false); + } + if (!String.IsNullOrEmpty(licenseFile)) { this.Core.CreateWixVariableRow(sourceLineNumbers, "WixExtbaLicenseRtf", licenseFile, false); @@ -334,7 +404,7 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions this.Core.CreateWixVariableRow(sourceLineNumbers, "WixExtbaThemeWxl", localizationFile, false); } - if (YesNoType.Yes == suppressOptionsUI || YesNoType.Yes == suppressDowngradeFailure || YesNoType.Yes == suppressRepair) + if (YesNoType.Yes == suppressOptionsUI || YesNoType.Yes == suppressDowngradeFailure || YesNoType.Yes == suppressRepair || YesNoType.Yes == showVersion || YesNoType.Yes == supportCacheOnly || YesNoType.Yes == showFilesInUse) { Row row = this.Core.CreateRow(sourceLineNumbers, "WixExtbaOptions"); if (YesNoType.Yes == suppressOptionsUI) @@ -357,15 +427,25 @@ namespace Microsoft.Tools.WindowsInstallerXml.Extensions row[3] = 1; } - if (YesNoType.Yes == launchPassive) + if (YesNoType.Yes == showFilesInUse) { row[4] = 1; } - if (YesNoType.Yes == launchQuiet) + if (YesNoType.Yes == supportCacheOnly) { row[5] = 1; } + + if (YesNoType.Yes == launchPassive) + { + row[6] = 1; + } + + if (YesNoType.Yes == launchQuiet) + { + row[7] = 1; + } } } } diff --git a/wixext/Xsd/bal.xsd b/wixext/Xsd/bal.xsd index 775bdf2..483f29b 100644 --- a/wixext/Xsd/bal.xsd +++ b/wixext/Xsd/bal.xsd @@ -62,7 +62,46 @@ - If set, the success page will show a Launch button the user can use to launch the application being installed. The string value can be formatted using Burn variables enclosed in brackets, to refer to installation directories and so forth. + + If set, the success page will show a Launch button the user can use to launch the application being installed. + The string value can be formatted using Burn variables enclosed in brackets, + to refer to installation directories and so forth. + + + + + + + Id of the target ApprovedExeForElevation element. + If set with LaunchTarget, WixStdBA will launch the application through the Engine's LaunchApprovedExe method instead of through ShellExecute. + + + + + + + If set, WixStdBA will supply these arguments when launching the application specified by the LaunchTarget attribute. + The string value can be formatted using Burn variables enclosed in brackets, + to refer to installation directories and so forth. + + + + + + + If set to "yes", WixStdBA will launch the application specified by the LaunchTarget attribute with the SW_HIDE flag. + This attribute is ignored when the LaunchTargetElevatedId attribute is specified. + + + + + + + WixStdBA will use this working folder when launching the specified application. + The string value can be formatted using Burn variables enclosed in brackets, + to refer to installation directories and so forth. + This attribute is ignored when the LaunchTargetElevatedId attribute is specified. + @@ -126,10 +165,34 @@ If set to "yes", the application version will be displayed on the UI. + + + If set to "yes", the bundle can be pre-cached using the /cache command line argument. + + + + + If set to "yes", WixStdBA will show a page allowing the user to restart applications when files are in use. + + - + + + + When set to "yes", the Prereq BA will plan the package to be installed if its InstallCondition is "true" or empty. + + + + + + + + + + + When set to "yes", lets the user override the variable's default value by specifying another value on the command line, diff --git a/wixext/data/tables.xml b/wixext/data/tables.xml index 7216e89..4d15f8c 100644 --- a/wixext/data/tables.xml +++ b/wixext/data/tables.xml @@ -27,6 +27,10 @@ maxValue="1" description="If 1, don't show Repair button during maintenance."/> + + #(loc.OptionsOkButton) + + #(loc.Title) + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + #(loc.Title) @@ -58,7 +69,10 @@ #(loc.Title) - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) #(loc.SuccessRestartText) @@ -67,7 +81,10 @@ #(loc.Title) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) #(loc.FailureRestartText) diff --git a/wixstdba/Resources/HyperlinkTheme.wxl b/wixstdba/Resources/HyperlinkTheme.wxl index 41910b6..6617b01 100644 --- a/wixstdba/Resources/HyperlinkTheme.wxl +++ b/wixstdba/Resources/HyperlinkTheme.wxl @@ -7,6 +7,7 @@ Version [WixBundleVersion] Version [WixBundleVersion] <a href="#">upgrade available</a> Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -35,14 +36,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel \ No newline at end of file diff --git a/wixstdba/Resources/HyperlinkTheme.xml b/wixstdba/Resources/HyperlinkTheme.xml index 34aca2c..e41c164 100644 --- a/wixstdba/Resources/HyperlinkTheme.xml +++ b/wixstdba/Resources/HyperlinkTheme.xml @@ -29,12 +29,23 @@ #(loc.OptionsHeader) - #(loc.OptionsLocationLabel) + #(loc.OptionsLocationLabel) + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + + + #(loc.ProgressHeader) #(loc.ProgressLabel) @@ -49,17 +60,23 @@ - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) - #(loc.SuccessRestartText) + #(loc.SuccessRestartText) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) - #(loc.FailureRestartText) + #(loc.FailureRestartText) diff --git a/wixstdba/Resources/RtfTheme.wxl b/wixstdba/Resources/RtfTheme.wxl index 67b5ee0..706b8bf 100644 --- a/wixstdba/Resources/RtfTheme.wxl +++ b/wixstdba/Resources/RtfTheme.wxl @@ -2,7 +2,9 @@ [WixBundleName] Setup [WixBundleName] + Version [WixBundleVersion] Are you sure you want to cancel? + Previous version Setup Help /install | /repair | /uninstall | /layout [directory] - installs, repairs, uninstalls or creates a complete local copy of the bundle in directory. Install is the default. @@ -30,14 +32,26 @@ &Repair &Uninstall &Close + Repair Successfully Completed + Uninstall Successfully Completed + Installation Successfully Completed Setup Successful &Launch You must restart your computer before you can use the software. &Restart &Close Setup Failed + Setup Failed + Uninstall Failed + Repair Failed One or more issues caused the setup to fail. Please fix the issues and then retry setup. For more information see the <a href="#">log file</a>. You must restart your computer to complete the rollback of the software. &Restart &Close + Files In Use + The following applications are using files that need to be updated: + Close the &applications and attempt to restart them. + &Do not close applications. A reboot will be required. + &OK + &Cancel diff --git a/wixstdba/Resources/RtfTheme.xml b/wixstdba/Resources/RtfTheme.xml index af724a8..53489db 100644 --- a/wixstdba/Resources/RtfTheme.xml +++ b/wixstdba/Resources/RtfTheme.xml @@ -31,6 +31,15 @@ + + #(loc.FilesInUseHeader) + #(loc.FilesInUseLabel) + + + + + + #(loc.ProgressHeader) #(loc.ProgressLabel) @@ -45,17 +54,23 @@ - #(loc.SuccessHeader) + #(loc.SuccessHeader) + #(loc.SuccessInstallHeader) + #(loc.SuccessRepairHeader) + #(loc.SuccessUninstallHeader) - #(loc.SuccessRestartText) + #(loc.SuccessRestartText) - #(loc.FailureHeader) + #(loc.FailureHeader) + #(loc.FailureInstallHeader) + #(loc.FailureUninstallHeader) + #(loc.FailureRepairHeader) #(loc.FailureHyperlinkLogText) - #(loc.FailureRestartText) + #(loc.FailureRestartText) diff --git a/wixstdba/WixStandardBootstrapperApplication.cpp b/wixstdba/WixStandardBootstrapperApplication.cpp index d3a609e..4916e3a 100644 --- a/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/wixstdba/WixStandardBootstrapperApplication.cpp @@ -16,1340 +16,1667 @@ static const LPCWSTR WIXSTDBA_WINDOW_CLASS = L"WixExtBA"; static const LPCWSTR WIXSTDBA_VARIABLE_INSTALL_FOLDER = L"InstallFolder"; static const LPCWSTR WIXSTDBA_VARIABLE_INSTALL_FOLDER2 = L"InstallFolder2"; static const LPCWSTR WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH = L"LaunchTarget"; +static const LPCWSTR WIXSTDBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID = L"LaunchTargetElevatedId"; static const LPCWSTR WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS = L"LaunchArguments"; static const LPCWSTR WIXSTDBA_VARIABLE_LAUNCH_HIDDEN = L"LaunchHidden"; +static const LPCWSTR WIXSTDBA_VARIABLE_LAUNCH_WORK_FOLDER = L"LaunchWorkingFolder"; + static const DWORD WIXSTDBA_ACQUIRE_PERCENTAGE = 30; +static const LPCWSTR WIXSTDBA_VARIABLE_BUNDLE_FILE_VERSION = L"WixBundleFileVersion"; +static const LPCWSTR WIXSTDBA_VARIABLE_LANGUAGE_ID = L"WixStdBALanguageId"; + enum WIXSTDBA_STATE { - WIXSTDBA_STATE_OPTIONS, - WIXSTDBA_STATE_INITIALIZING, - WIXSTDBA_STATE_INITIALIZED, - WIXSTDBA_STATE_HELP, - WIXSTDBA_STATE_DETECTING, - WIXSTDBA_STATE_DETECTED, - WIXSTDBA_STATE_PLANNING, - WIXSTDBA_STATE_PLANNED, - WIXSTDBA_STATE_APPLYING, - WIXSTDBA_STATE_CACHING, - WIXSTDBA_STATE_CACHED, - WIXSTDBA_STATE_EXECUTING, - WIXSTDBA_STATE_EXECUTED, - WIXSTDBA_STATE_APPLIED, - WIXSTDBA_STATE_FAILED, + WIXSTDBA_STATE_OPTIONS, + WIXSTDBA_STATE_FILESINUSE, + WIXSTDBA_STATE_INITIALIZING, + WIXSTDBA_STATE_INITIALIZED, + WIXSTDBA_STATE_HELP, + WIXSTDBA_STATE_DETECTING, + WIXSTDBA_STATE_DETECTED, + WIXSTDBA_STATE_PLANNING, + WIXSTDBA_STATE_PLANNED, + WIXSTDBA_STATE_APPLYING, + WIXSTDBA_STATE_CACHING, + WIXSTDBA_STATE_CACHED, + WIXSTDBA_STATE_EXECUTING, + WIXSTDBA_STATE_EXECUTED, + WIXSTDBA_STATE_APPLIED, + WIXSTDBA_STATE_FAILED, }; enum WM_WIXSTDBA { - WM_WIXSTDBA_SHOW_HELP = WM_APP + 100, - WM_WIXSTDBA_DETECT_PACKAGES, - WM_WIXSTDBA_PLAN_PACKAGES, - WM_WIXSTDBA_APPLY_PACKAGES, - WM_WIXSTDBA_CHANGE_STATE, + WM_WIXSTDBA_SHOW_HELP = WM_APP + 100, + WM_WIXSTDBA_DETECT_PACKAGES, + WM_WIXSTDBA_PLAN_PACKAGES, + WM_WIXSTDBA_APPLY_PACKAGES, + WM_WIXSTDBA_CHANGE_STATE, + WM_WIXSTDBA_SHOW_STATE_MODAL, + WM_WIXSTDBA_CLOSE_STATE_MODAL, + WM_WIXSTDBA_SHOW_FAILURE, }; // This enum must be kept in the same order as the vrgwzPageNames array. enum WIXSTDBA_PAGE { - WIXSTDBA_PAGE_LOADING, - WIXSTDBA_PAGE_HELP, - WIXSTDBA_PAGE_INSTALL, - WIXSTDBA_PAGE_OPTIONS, - WIXSTDBA_PAGE_MODIFY, - WIXSTDBA_PAGE_PROGRESS, - WIXSTDBA_PAGE_PROGRESS_PASSIVE, - WIXSTDBA_PAGE_SUCCESS, - WIXSTDBA_PAGE_FAILURE, - COUNT_WIXSTDBA_PAGE, + WIXSTDBA_PAGE_LOADING, + WIXSTDBA_PAGE_HELP, + WIXSTDBA_PAGE_INSTALL, + WIXSTDBA_PAGE_OPTIONS, + WIXSTDBA_PAGE_FILESINUSE, + WIXSTDBA_PAGE_MODIFY, + WIXSTDBA_PAGE_PROGRESS, + WIXSTDBA_PAGE_PROGRESS_PASSIVE, + WIXSTDBA_PAGE_SUCCESS, + WIXSTDBA_PAGE_FAILURE, + COUNT_WIXSTDBA_PAGE, }; // This array must be kept in the same order as the WIXSTDBA_PAGE enum. static LPCWSTR vrgwzPageNames[] = { - L"Loading", - L"Help", - L"Install", - L"Options", - L"Modify", - L"Progress", - L"ProgressPassive", - L"Success", - L"Failure", + L"Loading", + L"Help", + L"Install", + L"Options", + L"FilesInUse", + L"Modify", + L"Progress", + L"ProgressPassive", + L"Success", + L"Failure", }; enum WIXSTDBA_CONTROL { - // Non-paged controls - WIXSTDBA_CONTROL_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID, - WIXSTDBA_CONTROL_MINIMIZE_BUTTON, + // Non-paged controls + WIXSTDBA_CONTROL_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID, + WIXSTDBA_CONTROL_MINIMIZE_BUTTON, - // Help page - WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON, + // Help page + WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON, - // Welcome page - WIXSTDBA_CONTROL_INSTALL_BUTTON, - WIXSTDBA_CONTROL_OPTIONS_BUTTON, - WIXSTDBA_CONTROL_EULA_RICHEDIT, - WIXSTDBA_CONTROL_EULA_LINK, - WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, - WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON, - WIXSTDBA_CONTROL_VERSION_LABEL, - WIXSTDBA_CONTROL_UPGRADE_LINK, + // Welcome page + WIXSTDBA_CONTROL_INSTALL_BUTTON, + WIXSTDBA_CONTROL_OPTIONS_BUTTON, + WIXSTDBA_CONTROL_EULA_RICHEDIT, + WIXSTDBA_CONTROL_EULA_LINK, + WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, + WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON, + WIXSTDBA_CONTROL_VERSION_LABEL, + WIXSTDBA_CONTROL_UPGRADE_LINK, - // Options page - WIXSTDBA_CONTROL_FOLDER_EDITBOX, - WIXSTDBA_CONTROL_BROWSE_BUTTON, - WIXSTDBA_CONTROL_FOLDER_EDITBOX2, - WIXSTDBA_CONTROL_BROWSE_BUTTON2, - WIXSTDBA_CONTROL_OK_BUTTON, - WIXSTDBA_CONTROL_CANCEL_BUTTON, + // Options page + WIXSTDBA_CONTROL_FOLDER_EDITBOX, + WIXSTDBA_CONTROL_BROWSE_BUTTON, + WIXSTDBA_CONTROL_FOLDER_EDITBOX2, + WIXSTDBA_CONTROL_BROWSE_BUTTON2, + WIXSTDBA_CONTROL_OK_BUTTON, + WIXSTDBA_CONTROL_CANCEL_BUTTON, - // Modify page - WIXSTDBA_CONTROL_REPAIR_BUTTON, - WIXSTDBA_CONTROL_UNINSTALL_BUTTON, - WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON, + // FilesInUse page + WIXSTDBA_CONTROL_FILESINUSE_TEXT, + WIXSTDBA_CONTROL_FILESINUSE_CLOSE_RADIOBUTTON, + WIXSTDBA_CONTROL_FILESINUSE_DONTCLOSE_RADIOBUTTON, + WIXSTDBA_CONTROL_FILESINUSE_OK_BUTTON, + WIXSTDBA_CONTROL_FILESINUSE_CANCEL_BUTTON, - // Progress page - WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, - WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, - WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, + // Modify page + WIXSTDBA_CONTROL_REPAIR_BUTTON, + WIXSTDBA_CONTROL_UNINSTALL_BUTTON, + WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON, - WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, - WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, - WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, - WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, + // Progress page + WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, + WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, + WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, - WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, - WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, - WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, - WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, + WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, + WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, + WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, + WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, - WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, + WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, + WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, + WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, + WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, - // Success page - WIXSTDBA_CONTROL_LAUNCH_BUTTON, - WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, - WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, - WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON, + WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, - // Failure page - WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, - WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, - WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, - WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, - WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON, + // Success page + WIXSTDBA_CONTROL_LAUNCH_BUTTON, + WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, + WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, + WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON, + + WIXSTDBA_CONTROL_SUCCESS_HEADER, + WIXSTDBA_CONTROL_SUCCESS_INSTALL_HEADER, + WIXSTDBA_CONTROL_SUCCESS_UNINSTALL_HEADER, + WIXSTDBA_CONTROL_SUCCESS_REPAIR_HEADER, + + // Failure page + WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, + WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, + WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, + WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, + WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON, + + WIXSTDBA_CONTROL_FAILURE_HEADER, + WIXSTDBA_CONTROL_FAILURE_INSTALL_HEADER, + WIXSTDBA_CONTROL_FAILURE_UNINSTALL_HEADER, + WIXSTDBA_CONTROL_FAILURE_REPAIR_HEADER, }; static THEME_ASSIGN_CONTROL_ID vrgInitControls[] = { - { WIXSTDBA_CONTROL_CLOSE_BUTTON, L"CloseButton" }, - { WIXSTDBA_CONTROL_MINIMIZE_BUTTON, L"MinimizeButton" }, + { WIXSTDBA_CONTROL_CLOSE_BUTTON, L"CloseButton" }, + { WIXSTDBA_CONTROL_MINIMIZE_BUTTON, L"MinimizeButton" }, - { WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON, L"HelpCancelButton" }, + { WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON, L"HelpCancelButton" }, - { WIXSTDBA_CONTROL_INSTALL_BUTTON, L"InstallButton" }, - { WIXSTDBA_CONTROL_OPTIONS_BUTTON, L"OptionsButton" }, - { WIXSTDBA_CONTROL_EULA_RICHEDIT, L"EulaRichedit" }, - { WIXSTDBA_CONTROL_EULA_LINK, L"EulaHyperlink" }, - { WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, L"EulaAcceptCheckbox" }, - { WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON, L"WelcomeCancelButton" }, - { WIXSTDBA_CONTROL_VERSION_LABEL, L"InstallVersion" }, - { WIXSTDBA_CONTROL_UPGRADE_LINK, L"UpgradeHyperlink" }, + { WIXSTDBA_CONTROL_INSTALL_BUTTON, L"InstallButton" }, + { WIXSTDBA_CONTROL_OPTIONS_BUTTON, L"OptionsButton" }, + { WIXSTDBA_CONTROL_EULA_RICHEDIT, L"EulaRichedit" }, + { WIXSTDBA_CONTROL_EULA_LINK, L"EulaHyperlink" }, + { WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, L"EulaAcceptCheckbox" }, + { WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON, L"WelcomeCancelButton" }, + { WIXSTDBA_CONTROL_VERSION_LABEL, L"InstallVersion" }, + { WIXSTDBA_CONTROL_UPGRADE_LINK, L"UpgradeHyperlink" }, - { WIXSTDBA_CONTROL_FOLDER_EDITBOX, L"FolderEditbox" }, - { WIXSTDBA_CONTROL_BROWSE_BUTTON, L"BrowseButton" }, - { WIXSTDBA_CONTROL_FOLDER_EDITBOX2, L"FolderEditbox2" }, - { WIXSTDBA_CONTROL_BROWSE_BUTTON2, L"BrowseButton2" }, - { WIXSTDBA_CONTROL_OK_BUTTON, L"OptionsOkButton" }, - { WIXSTDBA_CONTROL_CANCEL_BUTTON, L"OptionsCancelButton" }, + { WIXSTDBA_CONTROL_FOLDER_EDITBOX, L"FolderEditbox" }, + { WIXSTDBA_CONTROL_BROWSE_BUTTON, L"BrowseButton" }, + { WIXSTDBA_CONTROL_FOLDER_EDITBOX2, L"FolderEditbox2" }, + { WIXSTDBA_CONTROL_BROWSE_BUTTON2, L"BrowseButton2" }, + { WIXSTDBA_CONTROL_OK_BUTTON, L"OptionsOkButton" }, + { WIXSTDBA_CONTROL_CANCEL_BUTTON, L"OptionsCancelButton" }, - { WIXSTDBA_CONTROL_REPAIR_BUTTON, L"RepairButton" }, - { WIXSTDBA_CONTROL_UNINSTALL_BUTTON, L"UninstallButton" }, - { WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" }, + { WIXSTDBA_CONTROL_FILESINUSE_TEXT, L"FilesInUseText" }, + { WIXSTDBA_CONTROL_FILESINUSE_CLOSE_RADIOBUTTON, L"FilesInUseCloseRadioButton" }, + { WIXSTDBA_CONTROL_FILESINUSE_DONTCLOSE_RADIOBUTTON, L"FilesInUseDontCloseRadioButton" }, + { WIXSTDBA_CONTROL_FILESINUSE_OK_BUTTON, L"FilesInUseOkButton" }, + { WIXSTDBA_CONTROL_FILESINUSE_CANCEL_BUTTON, L"FilesInUseCancelButton" }, - { WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" }, - { WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, L"CacheProgressbar" }, - { WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, L"CacheProgressText" }, - { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" }, - { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" }, - { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" }, - { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText" }, - { WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" }, - { WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, L"OverallProgressbar" }, - { WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" }, - { WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, L"OverallProgressText" }, - { WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" }, + { WIXSTDBA_CONTROL_REPAIR_BUTTON, L"RepairButton" }, + { WIXSTDBA_CONTROL_UNINSTALL_BUTTON, L"UninstallButton" }, + { WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" }, - { WIXSTDBA_CONTROL_LAUNCH_BUTTON, L"LaunchButton" }, - { WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, L"SuccessRestartText" }, - { WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" }, - { WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" }, + { WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" }, + { WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, L"CacheProgressbar" }, + { WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, L"CacheProgressText" }, + { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" }, + { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" }, + { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" }, + { WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText"}, + { WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" }, + { WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, L"OverallProgressbar" }, + { WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" }, + { WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, L"OverallProgressText" }, + { WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" }, - { WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" }, - { WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, L"FailureMessageText" }, - { WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, L"FailureRestartText" }, - { WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, L"FailureRestartButton" }, - { WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON, L"FailureCloseButton" }, + { WIXSTDBA_CONTROL_LAUNCH_BUTTON, L"LaunchButton" }, + { WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, L"SuccessRestartText" }, + { WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" }, + { WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" }, + + { WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" }, + { WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, L"FailureMessageText" }, + { WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, L"FailureRestartText" }, + { WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, L"FailureRestartButton" }, + { WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON, L"FailureCloseButton" }, + + { WIXSTDBA_CONTROL_SUCCESS_HEADER, L"SuccessHeader" }, + { WIXSTDBA_CONTROL_SUCCESS_INSTALL_HEADER, L"SuccessInstallHeader" }, + { WIXSTDBA_CONTROL_SUCCESS_UNINSTALL_HEADER, L"SuccessUninstallHeader" }, + { WIXSTDBA_CONTROL_SUCCESS_REPAIR_HEADER, L"SuccessRepairHeader" }, + + { WIXSTDBA_CONTROL_FAILURE_HEADER, L"FailureHeader" }, + { WIXSTDBA_CONTROL_FAILURE_INSTALL_HEADER, L"FailureInstallHeader" }, + { WIXSTDBA_CONTROL_FAILURE_UNINSTALL_HEADER, L"FailureUninstallHeader" }, + { WIXSTDBA_CONTROL_FAILURE_REPAIR_HEADER, L"FailureRepairHeader" }, }; +typedef struct _WIXSTDBA_PREREQ_PACKAGE +{ + LPWSTR sczPackageId; + BOOL fAlwaysInstall; + BOOL fWasAlreadyInstalled; + BOOL fPlannedToBeInstalled; + BOOL fSuccessfullyInstalled; +} WIXSTDBA_PREREQ_PACKAGE; + class CWixStandardBootstrapperApplication : public CBalBaseBootstrapperApplication { public: // IBootstrapperApplication - virtual STDMETHODIMP OnStartup() - { - HRESULT hr = S_OK; - DWORD dwUIThreadId = 0; + virtual STDMETHODIMP OnStartup() + { + HRESULT hr = S_OK; + DWORD dwUIThreadId = 0; - // create UI thread - m_hUiThread = ::CreateThread(NULL, 0, UiThreadProc, this, 0, &dwUIThreadId); - if (!m_hUiThread) - { - ExitWithLastError(hr, "Failed to create UI thread."); - } + // create UI thread + m_hUiThread = ::CreateThread(NULL, 0, UiThreadProc, this, 0, &dwUIThreadId); + if (!m_hUiThread) + { + ExitWithLastError(hr, "Failed to create UI thread."); + } - LExit: - return hr; - } + LExit: + return hr; + } - virtual STDMETHODIMP_(int) OnShutdown() - { - int nResult = IDNOACTION; + virtual STDMETHODIMP_(int) OnShutdown() + { + int nResult = IDNOACTION; - // wait for UI thread to terminate - if (m_hUiThread) - { - ::WaitForSingleObject(m_hUiThread, INFINITE); - ReleaseHandle(m_hUiThread); - } + // wait for UI thread to terminate + if (m_hUiThread) + { + ::WaitForSingleObject(m_hUiThread, INFINITE); + ReleaseHandle(m_hUiThread); + } - // If a restart was required. - if (m_fRestartRequired) - { - if (m_fAllowRestart) - { - nResult = IDRESTART; - } + // If a restart was required. + if (m_fRestartRequired) + { + if (m_fAllowRestart) + { + nResult = IDRESTART; + } - if (m_sczPrereqPackage) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, m_fAllowRestart ? "The prerequisites scheduled a restart. The bootstrapper application will be reloaded after the computer is restarted." - : "A restart is required by the prerequisites but the user delayed it. The bootstrapper application will be reloaded after the computer is restarted."); - } - } - else if (m_fPrereqInstalled) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were successfully installed. The bootstrapper application will be reloaded."); - nResult = IDRELOAD_BOOTSTRAPPER; - } - else if (m_fPrereqAlreadyInstalled) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop."); - } - else if (m_fPrereq) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were not successfully installed, error: 0x%x. The bootstrapper application will be not reloaded.", m_hrFinal); - } + if (m_fPrereq) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, m_fAllowRestart ? "The prerequisites scheduled a restart. The bootstrapper application will be reloaded after the computer is restarted." + : "A restart is required by the prerequisites but the user delayed it. The bootstrapper application will be reloaded after the computer is restarted."); + } + } + else if (m_fPrereqInstalled) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were successfully installed. The bootstrapper application will be reloaded."); + nResult = IDRELOAD_BOOTSTRAPPER; + } + else if (m_fPrereqAlreadyInstalled) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were already installed. The bootstrapper application will not be reloaded to prevent an infinite loop."); + } + else if (m_fPrereq) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "The prerequisites were not successfully installed, error: 0x%x. The bootstrapper application will be not reloaded.", m_hrFinal); + } - return nResult; - } + return nResult; + } - virtual STDMETHODIMP_(int) OnDetectRelatedBundle( - __in LPCWSTR wzBundleId, - __in BOOTSTRAPPER_RELATION_TYPE relationType, - __in LPCWSTR /*wzBundleTag*/, - __in BOOL fPerMachine, - __in DWORD64 /*dw64Version*/, - __in BOOTSTRAPPER_RELATED_OPERATION operation - ) - { - BalInfoAddRelatedBundleAsPackage(&m_Bundle.packages, wzBundleId, relationType, fPerMachine); + virtual STDMETHODIMP_(int) OnDetectRelatedBundle( + __in LPCWSTR wzBundleId, + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in LPCWSTR /*wzBundleTag*/, + __in BOOL fPerMachine, + __in DWORD64 /*dw64Version*/, + __in BOOTSTRAPPER_RELATED_OPERATION operation + ) + { + BalInfoAddRelatedBundleAsPackage(&m_Bundle.packages, wzBundleId, relationType, fPerMachine); - // If we're not doing a pre-req install, remember when our bundle would cause a downgrade. - if (!m_sczPrereqPackage && BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) - { - m_fDowngrading = TRUE; - } + // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade. + if (!m_fPrereq && BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) + { + m_fDowngrading = TRUE; + } - return CheckCanceled() ? IDCANCEL : IDOK; - } + return CheckCanceled() ? IDCANCEL : IDOK; + } - virtual STDMETHODIMP_(void) OnDetectPackageComplete( - __in LPCWSTR wzPackageId, - __in HRESULT /*hrStatus*/, - __in BOOTSTRAPPER_PACKAGE_STATE state - ) - { - // If the prereq package is already installed, remember that. - if (m_sczPrereqPackage && BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state && - CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, m_sczPrereqPackage, -1)) - { - m_fPrereqAlreadyInstalled = TRUE; - } - } + virtual STDMETHODIMP_(void) OnDetectPackageComplete( + __in LPCWSTR wzPackageId, + __in HRESULT /*hrStatus*/, + __in BOOTSTRAPPER_PACKAGE_STATE state + ) + { + WIXSTDBA_PREREQ_PACKAGE* pPrereqPackage = NULL; + BAL_INFO_PACKAGE* pPackage = NULL; + HRESULT hr = GetPrereqPackage(wzPackageId, &pPrereqPackage, &pPackage); + if (SUCCEEDED(hr) && BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state) + { + // If the prerequisite package is already installed, remember that. + pPrereqPackage->fWasAlreadyInstalled = TRUE; + } + } - // OnDetectUpdateBegin - called when the engine begins detection for bundle update. - virtual STDMETHODIMP_(int) OnDetectUpdateBegin( - __in_z LPCWSTR wzUpdateLocation, - __in int nRecommendation - ) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Update location: %ls.", wzUpdateLocation); + virtual STDMETHODIMP_(void) OnDetectComplete( + __in HRESULT hrStatus + ) + { + if (SUCCEEDED(hrStatus) && m_pBAFunction) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect complete BA function"); + m_pBAFunction->OnDetectComplete(); + } - m_wzUpdateLocation = wzUpdateLocation; - // If there is an upgrade link, check for update on a background thread - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK)) - { - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK, FALSE); - ::CreateThread(NULL, 0, ThreadProc, this, 0, NULL); - } + if (SUCCEEDED(hrStatus)) + { + hrStatus = EvaluateConditions(); + + if (m_fPrereq) + { + m_fPrereqAlreadyInstalled = TRUE; - return nRecommendation; - } + // At this point we have to assume that all prerequisite packages need to be installed, so set to false if any of them aren't installed. + for (DWORD i = 0; i < m_cPrereqPackages; ++i) + { + if (m_rgPrereqPackages[i].sczPackageId && !m_rgPrereqPackages[i].fWasAlreadyInstalled) + { + m_fPrereqAlreadyInstalled = FALSE; + break; + } + } + } + } + + SetState(WIXSTDBA_STATE_DETECTED, hrStatus); + + if (BOOTSTRAPPER_ACTION_CACHE == m_plannedAction) + { + if (m_fSupportCacheOnly) + { + // Doesn't make sense to prompt the user if cache only is requested. + if (BOOTSTRAPPER_DISPLAY_PASSIVE < m_command.display) + { + m_command.display = BOOTSTRAPPER_DISPLAY_PASSIVE; + } + + m_command.action = BOOTSTRAPPER_ACTION_CACHE; + } + else + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to only cache a bundle that does not explicitly support it."); + } + } + + // If we're not interacting with the user or we're doing a layout or we're just after a force restart + // then automatically start planning. + if (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || BOOTSTRAPPER_ACTION_LAYOUT == m_command.action || BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType) + { + if (SUCCEEDED(hrStatus)) + { + ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); + } + } + } - virtual STDMETHODIMP_(int) OnDetectBegin( - __in BOOL /*fInstalled*/, - __in DWORD /*cPackages*/ - ) - { - return IDNOACTION; - } + virtual STDMETHODIMP_(int) OnPlanRelatedBundle( + __in_z LPCWSTR /*wzBundleId*/, + __inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState + ) + { + // If we're only installing prerequisites, do not touch related bundles. + if (m_fPrereq) + { + *pRequestedState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + + return CheckCanceled() ? IDCANCEL : IDOK; + } - virtual STDMETHODIMP_(void) OnDetectComplete( - __in HRESULT hrStatus - ) - { - if (SUCCEEDED(hrStatus) && m_pBAFunction) - { - m_pBAFunction->OnDetectComplete(); - } + virtual STDMETHODIMP_(int) OnPlanPackageBegin( + __in_z LPCWSTR wzPackageId, + __inout BOOTSTRAPPER_REQUEST_STATE *pRequestState + ) + { + HRESULT hr = S_OK; + WIXSTDBA_PREREQ_PACKAGE* pPrereqPackage = NULL; + BAL_INFO_PACKAGE* pPackage = NULL; - if (SUCCEEDED(hrStatus)) - { - hrStatus = EvaluateConditions(); - } + // If we're planning to install a prerequisite, install it. The prerequisite needs to be installed + // in all cases (even uninstall!) so the BA can load next. + if (m_fPrereq) + { + // Only install prerequisite packages, and check the InstallCondition on prerequisite support packages. + BOOL fInstall = FALSE; + hr = GetPrereqPackage(wzPackageId, &pPrereqPackage, &pPackage); + if (SUCCEEDED(hr) && pPackage) + { + if (pPrereqPackage->fAlwaysInstall) + { + fInstall = TRUE; + } + else if(pPackage->sczInstallCondition && *pPackage->sczInstallCondition) + { + hr = m_pEngine->EvaluateCondition(pPackage->sczInstallCondition, &fInstall); + if (FAILED(hr)) + { + fInstall = FALSE; + } + } + else + { + // If the InstallCondition is missing, then it should always be installed. + fInstall = TRUE; + } - SetState(WIXSTDBA_STATE_DETECTED, hrStatus); + pPrereqPackage->fPlannedToBeInstalled = fInstall; + } - // If we're not interacting with the user or we're doing a layout or we're just after a force restart - // then automatically start planning. - if (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || BOOTSTRAPPER_ACTION_LAYOUT == m_command.action || BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType) - { - if (SUCCEEDED(hrStatus)) - { - ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); - } - } - } + if (fInstall) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + else + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + } + else if (m_sczAfterForcedRestartPackage) // after force restart, skip packages until after the package that caused the restart. + { + // After restart we need to finish the dependency registration for our package so allow the package + // to go present. + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, m_sczAfterForcedRestartPackage, -1)) + { + // Do not allow a repair because that could put us in a perpetual restart loop. + if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) + { + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; + } + + ReleaseNullStr(m_sczAfterForcedRestartPackage); // no more skipping now. + } + else // not the matching package, so skip it. + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId); + + *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; + } + } + + return CheckCanceled() ? IDCANCEL : IDOK; + } - virtual STDMETHODIMP_(int) OnPlanRelatedBundle( - __in_z LPCWSTR /*wzBundleId*/, - __inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState - ) - { - // If we're only installing prereq, do not touch related bundles. - if (m_sczPrereqPackage) - { - *pRequestedState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } + virtual STDMETHODIMP_(void) OnPlanComplete( + __in HRESULT hrStatus + ) + { + if (SUCCEEDED(hrStatus) && m_pBAFunction) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan complete BA function"); + m_pBAFunction->OnPlanComplete(); + } - return CheckCanceled() ? IDCANCEL : IDOK; - } + if (m_fPrereq) + { + m_fPrereqAlreadyInstalled = TRUE; + + // Now that we've planned the packages, we can focus on the prerequisite packages that are supposed to be installed. + for (DWORD i = 0; i < m_cPrereqPackages; ++i) + { + if (m_rgPrereqPackages[i].sczPackageId && !m_rgPrereqPackages[i].fWasAlreadyInstalled && m_rgPrereqPackages[i].fPlannedToBeInstalled) + { + m_fPrereqAlreadyInstalled = FALSE; + break; + } + } + } + + SetState(WIXSTDBA_STATE_PLANNED, hrStatus); + + if (SUCCEEDED(hrStatus)) + { + ::PostMessageW(m_hWnd, WM_WIXSTDBA_APPLY_PACKAGES, 0, 0); + } + + m_fStartedExecution = FALSE; + m_dwCalculatedCacheProgress = 0; + m_dwCalculatedExecuteProgress = 0; + } - virtual STDMETHODIMP_(int) OnPlanPackageBegin( - __in_z LPCWSTR wzPackageId, - __inout BOOTSTRAPPER_REQUEST_STATE *pRequestState - ) - { - // If we're planning to install a pre-req, install it. The pre-req needs to be installed - // in all cases (even uninstall!) so the BA can load next. - if (m_sczPrereqPackage) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, m_sczPrereqPackage, -1)) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } - else // skip everything else. - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } - } - else if (m_sczAfterForcedRestartPackage) // after force restart skip packages until after the package that caused the restart. - { - // After restart we need to finish the dependency registration for our package so allow the package - // to go present. - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, m_sczAfterForcedRestartPackage, -1)) - { - // Do not allow a repair because that could put us in a perpetual restart loop. - if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) - { - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT; - } + virtual STDMETHODIMP_(int) OnCachePackageBegin( + __in_z LPCWSTR wzPackageId, + __in DWORD cCachePayloads, + __in DWORD64 dw64PackageCacheSize + ) + { + if (wzPackageId && *wzPackageId) + { + BAL_INFO_PACKAGE* pPackage = NULL; + HRESULT hr = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); + LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId; - ReleaseNullStr(m_sczAfterForcedRestartPackage); // no more skipping now. - } - else // not the matching package, so skip it. - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, wz); - *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; - } - } + // If something started executing, leave it in the overall progress text. + if (!m_fStartedExecution) + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, wz); + } + } - return CheckCanceled() ? IDCANCEL : IDOK; - } + return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize); + } - virtual STDMETHODIMP_(void) OnPlanComplete( - __in HRESULT hrStatus - ) - { - if (SUCCEEDED(hrStatus) && m_pBAFunction) - { - m_pBAFunction->OnPlanComplete(); - } - - SetState(WIXSTDBA_STATE_PLANNED, hrStatus); - - if (SUCCEEDED(hrStatus)) - { - ::PostMessageW(m_hWnd, WM_WIXSTDBA_APPLY_PACKAGES, 0, 0); - } - - m_dwCalculatedCacheProgress = 0; - m_dwCalculatedExecuteProgress = 0; - } - - - virtual STDMETHODIMP_(int) OnCachePackageBegin( - __in_z LPCWSTR wzPackageId, - __in DWORD cCachePayloads, - __in DWORD64 dw64PackageCacheSize - ) - { - if (wzPackageId && *wzPackageId) - { - BAL_INFO_PACKAGE* pPackage = NULL; - HRESULT hr = BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); - LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId; - - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, wz); - - // If something started executing, leave it in the overall progress text. - if (!m_fStartedExecution) - { - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, wz); - } - } - - return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize); - } - - - virtual STDMETHODIMP_(int) OnCacheAcquireProgress( - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in DWORD64 dw64Progress, - __in DWORD64 dw64Total, - __in DWORD dwOverallPercentage - ) - { - WCHAR wzProgress[5] = {}; + virtual STDMETHODIMP_(int) OnCacheAcquireProgress( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in DWORD64 dw64Progress, + __in DWORD64 dw64Total, + __in DWORD dwOverallPercentage + ) + { + WCHAR wzProgress[5] = { }; #ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); #endif - ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, wzProgress); + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_TEXT, wzProgress); - ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, dwOverallPercentage); + ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_BAR, dwOverallPercentage); - // Restrict progress to 100% to hide burn engine progress bug. - m_dwCalculatedCacheProgress = min(dwOverallPercentage, 100) * WIXSTDBA_ACQUIRE_PERCENTAGE / 100; + m_dwCalculatedCacheProgress = dwOverallPercentage * WIXSTDBA_ACQUIRE_PERCENTAGE / 100; + ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + + SetTaskbarButtonProgress(m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + + return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); + } + + + virtual STDMETHODIMP_(int) OnCacheAcquireComplete( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __in int nRecommendation + ) + { + SetProgressState(hrStatus); + return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation); + } + + + virtual STDMETHODIMP_(int) OnCacheVerifyComplete( + __in_z LPCWSTR wzPackageId, + __in_z LPCWSTR wzPayloadId, + __in HRESULT hrStatus, + __in int nRecommendation + ) + { + SetProgressState(hrStatus); + return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation); + } + + + virtual STDMETHODIMP_(void) OnCacheComplete( + __in HRESULT /*hrStatus*/ + ) + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, L""); + SetState(WIXSTDBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. + } + + + virtual STDMETHODIMP_(int) OnError( + __in BOOTSTRAPPER_ERROR_TYPE errorType, + __in LPCWSTR wzPackageId, + __in DWORD dwCode, + __in_z LPCWSTR wzError, + __in DWORD dwUIHint, + __in DWORD /*cData*/, + __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/, + __in int nRecommendation + ) + { + int nResult = nRecommendation; + LPWSTR sczError = NULL; + + if (BOOTSTRAPPER_DISPLAY_EMBEDDED == m_command.display) + { + HRESULT hr = m_pEngine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult); + if (FAILED(hr)) + { + nResult = IDERROR; + } + } + else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) + { + // If this is an authentication failure, let the engine try to handle it for us. + if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) + { + nResult = IDTRYAGAIN; + } + else // show a generic error message box. + { + BalRetryErrorOccurred(wzPackageId, dwCode); + + if (!m_fShowingInternalUiThisPackage) + { + // If no error message was provided, use the error code to try and get an error message. + if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) + { + HRESULT hr = StrAllocFromError(&sczError, dwCode, NULL); + if (FAILED(hr) || !sczError || !*sczError) + { + StrAllocFormatted(&sczError, L"0x%x", dwCode); + } + } + + nResult = ::MessageBoxW(m_hWnd, sczError ? sczError : wzError, m_pTheme->sczCaption, dwUIHint); + } + } + + SetProgressState(HRESULT_FROM_WIN32(dwCode)); + } + else // just take note of the error code and let things continue. + { + BalRetryErrorOccurred(wzPackageId, dwCode); + } + + ReleaseStr(sczError); + return nResult; + } + + + virtual STDMETHODIMP_(int) OnExecuteMsiMessage( + __in_z LPCWSTR wzPackageId, + __in INSTALLMESSAGE mt, + __in UINT uiFlags, + __in_z LPCWSTR wzMessage, + __in DWORD cData, + __in_ecount_z_opt(cData) LPCWSTR* rgwzData, + __in int nRecommendation + ) + { #ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnCacheAcquireProgress() - calculated progress: %u%%, displayed progress: %u%%", m_dwCalculatedCacheProgress, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage); #endif - ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) + { + int nResult = ::MessageBoxW(m_hWnd, wzMessage, m_pTheme->sczCaption, uiFlags); + return nResult; + } - SetTaskbarButtonProgress(m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + if (INSTALLMESSAGE_ACTIONSTART == mt) + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage); + } - return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage); - } + return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation); + } - virtual STDMETHODIMP_(int) OnCacheAcquireComplete( - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __in int nRecommendation - ) - { - SetProgressState(hrStatus); - return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation); - } - - - virtual STDMETHODIMP_(int) OnCacheVerifyComplete( - __in_z LPCWSTR wzPackageId, - __in_z LPCWSTR wzPayloadId, - __in HRESULT hrStatus, - __in int nRecommendation - ) - { - SetProgressState(hrStatus); - return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation); - } - - - virtual STDMETHODIMP_(void) OnCacheComplete( - __in HRESULT /*hrStatus*/ - ) - { - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_CACHE_PROGRESS_PACKAGE_TEXT, L""); - SetState(WIXSTDBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. - } - - - virtual STDMETHODIMP_(int) OnError( - __in BOOTSTRAPPER_ERROR_TYPE errorType, - __in LPCWSTR wzPackageId, - __in DWORD dwCode, - __in_z LPCWSTR wzError, - __in DWORD dwUIHint, - __in DWORD /*cData*/, - __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/, - __in int nRecommendation - ) - { - int nResult = nRecommendation; - LPWSTR sczError = NULL; - - if (BOOTSTRAPPER_DISPLAY_EMBEDDED == m_command.display) - { - HRESULT hr = m_pEngine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult); - if (FAILED(hr)) - { - nResult = IDERROR; - } - } - else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) - { - // If this is an authentication failure, let the engine try to handle it for us. - if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) - { - nResult = IDTRYAGAIN; - } - else // show a generic error message box. - { - BalRetryErrorOccurred(wzPackageId, dwCode); - - if (!m_fShowingInternalUiThisPackage) - { - // If no error message was provided, use the error code to try and get an error message. - if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) - { - HRESULT hr = StrAllocFromError(&sczError, dwCode, NULL); - if (FAILED(hr) || !sczError || !*sczError) - { - StrAllocFormatted(&sczError, L"0x%x", dwCode); - } - } - - nResult = ::MessageBoxW(m_hWnd, sczError ? sczError : wzError, m_pTheme->sczCaption, dwUIHint); - } - } - - SetProgressState(HRESULT_FROM_WIN32(dwCode)); - } - else // just take note of the error code and let things continue. - { - BalRetryErrorOccurred(wzPackageId, dwCode); - } - - ReleaseStr(sczError); - return nResult; - } - - - virtual STDMETHODIMP_(int) OnExecuteMsiMessage( - __in_z LPCWSTR wzPackageId, - __in INSTALLMESSAGE mt, - __in UINT uiFlags, - __in_z LPCWSTR wzMessage, - __in DWORD cData, - __in_ecount_z_opt(cData) LPCWSTR* rgwzData, - __in int nRecommendation - ) - { -#ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage); -#endif - if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) - { - int nResult = ::MessageBoxW(m_hWnd, wzMessage, m_pTheme->sczCaption, uiFlags); - return nResult; - } - - if (INSTALLMESSAGE_ACTIONSTART == mt) - { - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage); - } - - return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation); - } - - - virtual STDMETHODIMP_(int) OnProgress( - __in DWORD dwProgressPercentage, - __in DWORD dwOverallProgressPercentage - ) - { - WCHAR wzProgress[5] = {}; + virtual STDMETHODIMP_(int) OnProgress( + __in DWORD dwProgressPercentage, + __in DWORD dwOverallProgressPercentage + ) + { + WCHAR wzProgress[5] = { }; #ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage); #endif - ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, wzProgress); + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_TEXT, wzProgress); - ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage); - SetTaskbarButtonProgress(dwOverallProgressPercentage); + ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage); + SetTaskbarButtonProgress(dwOverallProgressPercentage); - return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage); - } + return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage); + } - virtual STDMETHODIMP_(int) OnExecutePackageBegin( - __in_z LPCWSTR wzPackageId, - __in BOOL fExecute - ) - { - LPWSTR sczFormattedString = NULL; + virtual STDMETHODIMP_(int) OnExecutePackageBegin( + __in_z LPCWSTR wzPackageId, + __in BOOL fExecute + ) + { + LPWSTR sczFormattedString = NULL; - m_fStartedExecution = TRUE; + m_fStartedExecution = TRUE; - if (wzPackageId && *wzPackageId) - { - BAL_INFO_PACKAGE* pPackage = NULL; - BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); + if (wzPackageId && *wzPackageId) + { + BAL_INFO_PACKAGE* pPackage = NULL; + BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); - LPCWSTR wz = wzPackageId; - if (pPackage) - { - LOC_STRING* pLocString = NULL; + LPCWSTR wz = wzPackageId; + if (pPackage) + { + LOC_STRING* pLocString = NULL; - switch (pPackage->type) - { - case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON: - LocGetString(m_pWixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString); - break; + switch (pPackage->type) + { + case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON: + LocGetString(m_pWixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString); + break; - case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH: - LocGetString(m_pWixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString); - break; + case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH: + LocGetString(m_pWixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString); + break; - case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE: - LocGetString(m_pWixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString); - break; - } + case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE: + LocGetString(m_pWixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString); + break; + } - if (pLocString) - { - BalFormatString(pLocString->wzText, &sczFormattedString); - } + if (pLocString) + { + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + BalFormatString(pLocString->wzText, &sczFormattedString); + } - wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId; - } + wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId; + } - m_fShowingInternalUiThisPackage = pPackage && pPackage->fDisplayInternalUI; + m_fShowingInternalUiThisPackage = pPackage && pPackage->fDisplayInternalUI; - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, wz); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, wz); - } - else - { - m_fShowingInternalUiThisPackage = FALSE; - } + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, wz); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, wz); + } + else + { + m_fShowingInternalUiThisPackage = FALSE; + } - ReleaseStr(sczFormattedString); - return __super::OnExecutePackageBegin(wzPackageId, fExecute); - } + ReleaseStr(sczFormattedString); + return __super::OnExecutePackageBegin(wzPackageId, fExecute); + } - virtual int __stdcall OnExecuteProgress( - __in_z LPCWSTR wzPackageId, - __in DWORD dwProgressPercentage, - __in DWORD dwOverallProgressPercentage - ) - { - WCHAR wzProgress[5] = {}; + virtual int __stdcall OnExecuteProgress( + __in_z LPCWSTR wzPackageId, + __in DWORD dwProgressPercentage, + __in DWORD dwOverallProgressPercentage + ) + { + WCHAR wzProgress[5] = { }; #ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); #endif - ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, wzProgress); + ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_TEXT, wzProgress); - ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage); + ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage); - m_dwCalculatedExecuteProgress = dwOverallProgressPercentage * (100 - WIXSTDBA_ACQUIRE_PERCENTAGE) / 100; -#ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: OnExecuteProgress() - calculated progress: %u%%, displayed progress: %u%%", m_dwCalculatedExecuteProgress, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); -#endif - ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + m_dwCalculatedExecuteProgress = dwOverallProgressPercentage * (100 - WIXSTDBA_ACQUIRE_PERCENTAGE) / 100; + ThemeSetProgressControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_CALCULATED_PROGRESS_BAR, m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); - SetTaskbarButtonProgress(m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); + SetTaskbarButtonProgress(m_dwCalculatedCacheProgress + m_dwCalculatedExecuteProgress); - return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); - } + return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage); + } - virtual STDMETHODIMP_(int) OnExecutePackageComplete( - __in_z LPCWSTR wzPackageId, - __in HRESULT hrExitCode, - __in BOOTSTRAPPER_APPLY_RESTART restart, - __in int nRecommendation - ) - { - SetProgressState(hrExitCode); + virtual STDMETHODIMP_(int) OnExecutePackageComplete( + __in_z LPCWSTR wzPackageId, + __in HRESULT hrExitCode, + __in BOOTSTRAPPER_APPLY_RESTART restart, + __in int nRecommendation + ) + { + SetProgressState(hrExitCode); - int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation); + int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation); - if (m_sczPrereqPackage && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, m_sczPrereqPackage, -1)) - { - m_fPrereqInstalled = SUCCEEDED(hrExitCode); + WIXSTDBA_PREREQ_PACKAGE* pPrereqPackage = NULL; + BAL_INFO_PACKAGE* pPackage; + HRESULT hr = GetPrereqPackage(wzPackageId, &pPrereqPackage, &pPackage); + if (SUCCEEDED(hr)) + { + pPrereqPackage->fSuccessfullyInstalled = SUCCEEDED(hrExitCode); - // If the pre-req required a restart (any restart) then do an immediate - // restart to ensure that the bundle will get launched again post reboot. - if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) - { - nResult = IDRESTART; - } - } + // If the prerequisite required a restart (any restart) then do an immediate + // restart to ensure that the bundle will get launched again post reboot. + if (BOOTSTRAPPER_APPLY_RESTART_NONE != restart) + { + nResult = IDRESTART; + } + } - return nResult; - } + return nResult; + } - virtual STDMETHODIMP_(void) OnExecuteComplete( - __in HRESULT hrStatus - ) - { - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, L""); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L""); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, L""); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel. + virtual STDMETHODIMP_(void) OnExecuteComplete( + __in HRESULT hrStatus + ) + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_PACKAGE_TEXT, L""); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L""); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_OVERALL_PROGRESS_PACKAGE_TEXT, L""); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel. - SetState(WIXSTDBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. - SetProgressState(hrStatus); - } + SetState(WIXSTDBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error. + SetProgressState(hrStatus); + } - virtual STDMETHODIMP_(int) OnResolveSource( - __in_z LPCWSTR wzPackageOrContainerId, - __in_z_opt LPCWSTR wzPayloadId, - __in_z LPCWSTR wzLocalSource, - __in_z_opt LPCWSTR wzDownloadSource - ) - { - int nResult = IDERROR; // assume we won't resolve source and that is unexpected. + virtual STDMETHODIMP_(int) OnResolveSource( + __in_z LPCWSTR wzPackageOrContainerId, + __in_z_opt LPCWSTR wzPayloadId, + __in_z LPCWSTR wzLocalSource, + __in_z_opt LPCWSTR wzDownloadSource + ) + { + int nResult = IDERROR; // assume we won't resolve source and that is unexpected. - if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) - { - if (wzDownloadSource) - { - nResult = IDDOWNLOAD; - } - else // prompt to change the source location. - { - OPENFILENAMEW ofn = {}; - WCHAR wzFile[MAX_PATH] = {}; + if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) + { + if (wzDownloadSource) + { + nResult = IDDOWNLOAD; + } + else // prompt to change the source location. + { + OPENFILENAMEW ofn = { }; + WCHAR wzFile[MAX_PATH] = { }; - ::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource); + ::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = m_hWnd; - ofn.lpstrFile = wzFile; - ofn.nMaxFile = countof(wzFile); - ofn.lpstrFilter = L"All Files\0*.*\0"; - ofn.nFilterIndex = 1; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; - ofn.lpstrTitle = m_pTheme->sczCaption; + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = m_hWnd; + ofn.lpstrFile = wzFile; + ofn.nMaxFile = countof(wzFile); + ofn.lpstrFilter = L"All Files\0*.*\0"; + ofn.nFilterIndex = 1; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + ofn.lpstrTitle = m_pTheme->sczCaption; - if (::GetOpenFileNameW(&ofn)) - { - HRESULT hr = m_pEngine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile); - nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR; - } - else - { - nResult = IDCANCEL; - } - } - } - else if (wzDownloadSource) - { - // If doing a non-interactive install and download source is available, let's try downloading the package silently - nResult = IDDOWNLOAD; - } - // else there's nothing more we can do in non-interactive mode + if (::GetOpenFileNameW(&ofn)) + { + HRESULT hr = m_pEngine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile); + nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR; + } + else + { + nResult = IDCANCEL; + } + } + } + else if (wzDownloadSource) + { + // If doing a non-interactive install and download source is available, let's try downloading the package silently + nResult = IDDOWNLOAD; + } + // else there's nothing more we can do in non-interactive mode - return CheckCanceled() ? IDCANCEL : nResult; - } + return CheckCanceled() ? IDCANCEL : nResult; + } - virtual STDMETHODIMP_(int) OnApplyComplete( - __in HRESULT hrStatus, - __in BOOTSTRAPPER_APPLY_RESTART restart - ) - { - m_restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI. + virtual STDMETHODIMP_(int) OnApplyComplete( + __in HRESULT hrStatus, + __in BOOTSTRAPPER_APPLY_RESTART restart + ) + { + m_restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI. - // If a restart was encountered and we are not suppressing restarts, then restart is required. - m_fRestartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < m_command.restart); - // If a restart is required and we're not displaying a UI or we are not supposed to prompt for restart then allow the restart. - m_fAllowRestart = m_fRestartRequired && (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || BOOTSTRAPPER_RESTART_PROMPT < m_command.restart); + // If a restart was encountered and we are not suppressing restarts, then restart is required. + m_fRestartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < m_command.restart); + // If a restart is required and we're not displaying a UI or we are not supposed to prompt for restart then allow the restart. + m_fAllowRestart = m_fRestartRequired && (BOOTSTRAPPER_DISPLAY_FULL > m_command.display || BOOTSTRAPPER_RESTART_PROMPT < m_command.restart); - // If we are showing UI, wait a beat before moving to the final screen. - if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) - { - ::Sleep(250); - } + if (m_fPrereq) + { + m_fPrereqInstalled = TRUE; + BOOL fInstalledAPackage = FALSE; - SetState(WIXSTDBA_STATE_APPLIED, hrStatus); - SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red + for (DWORD i = 0; i < m_cPrereqPackages; ++i) + { + if (m_rgPrereqPackages[i].sczPackageId && m_rgPrereqPackages[i].fPlannedToBeInstalled && !m_rgPrereqPackages[i].fWasAlreadyInstalled) + { + if (m_rgPrereqPackages[i].fSuccessfullyInstalled) + { + fInstalledAPackage = TRUE; + } + else + { + m_fPrereqInstalled = FALSE; + break; + } + } + } - // If we successfully applied an update close the window since the new Bundle should be running now. - if (SUCCEEDED(hrStatus) && m_fUpdating) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Update downloaded, close bundle."); - ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); - } + m_fPrereqInstalled = m_fPrereqInstalled && fInstalledAPackage; + } - return IDNOACTION; - } + // If we are showing UI, wait a beat before moving to the final screen. + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) + { + ::Sleep(250); + } + + SetState(WIXSTDBA_STATE_APPLIED, hrStatus); + SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red + + return IDNOACTION; + } + + virtual STDMETHODIMP_(void) OnLaunchApprovedExeComplete( + __in HRESULT hrStatus, + __in DWORD /*processId*/ + ) + { + if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hrStatus) + { + //try with ShelExec next time + OnClickLaunchButton(); + } + else + { + ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); + } + } + + virtual STDMETHODIMP_(int) OnExecuteFilesInUse( + __in_z LPCWSTR wzPackageId, + __in DWORD cFiles, + __in_ecount_z(cFiles) LPCWSTR* rgwzFiles + ) + { + if (m_fShowFilesInUse && !m_fPrereq && wzPackageId && *wzPackageId) + { + //If this is an MSI package, display the files in use page. + BAL_INFO_PACKAGE* pPackage = NULL; + BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); + + if (pPackage && BAL_INFO_PACKAGE_TYPE_MSI == pPackage->type) + { + return ShowFilesInUseModal(cFiles, rgwzFiles); + } + } + + return __super::OnExecuteFilesInUse(wzPackageId, cFiles, rgwzFiles); + } private: // privates - // - // UiThreadProc - entrypoint for UI thread. - // - static DWORD WINAPI UiThreadProc( - __in LPVOID pvContext - ) - { - HRESULT hr = S_OK; - CWixStandardBootstrapperApplication* pThis = (CWixStandardBootstrapperApplication*)pvContext; - BOOL fComInitialized = FALSE; - BOOL fRet = FALSE; - MSG msg = {}; - - // Initialize COM and theme. - hr = ::CoInitialize(NULL); - BalExitOnFailure(hr, "Failed to initialize COM."); - fComInitialized = TRUE; - - hr = ThemeInitialize(pThis->m_hModule); - BalExitOnFailure(hr, "Failed to initialize theme manager."); - - hr = pThis->InitializeData(); - BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application."); - - // Create main window. - pThis->InitializeTaskbarButton(); - hr = pThis->CreateMainWindow(); - BalExitOnFailure(hr, "Failed to create main window."); - - // Okay, we're ready for packages now. - pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr); - ::PostMessageW(pThis->m_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action ? WM_WIXSTDBA_SHOW_HELP : WM_WIXSTDBA_DETECT_PACKAGES, 0, 0); - - // message pump - while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) - { - if (-1 == fRet) - { - hr = E_UNEXPECTED; - BalExitOnFailure(hr, "Unexpected return value from message pump."); - } - else if (!ThemeHandleKeyboardMessage(pThis->m_pTheme, msg.hwnd, &msg)) - { - ::TranslateMessage(&msg); - ::DispatchMessageW(&msg); - } - } - - // Succeeded thus far, check to see if anything went wrong while actually - // executing changes. - if (FAILED(pThis->m_hrFinal)) - { - hr = pThis->m_hrFinal; - } - else if (pThis->CheckCanceled()) - { - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); - } - - LExit: - // destroy main window - pThis->DestroyMainWindow(); - - // initiate engine shutdown - DWORD dwQuit = HRESULT_CODE(hr); - if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->m_restartResult) - { - dwQuit = ERROR_SUCCESS_REBOOT_INITIATED; - } - else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->m_restartResult) - { - dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED; - } - pThis->m_pEngine->Quit(dwQuit); - - ReleaseTheme(pThis->m_pTheme); - ThemeUninitialize(); - - // uninitialize COM - if (fComInitialized) - { - ::CoUninitialize(); - } - - return hr; - } - - - static DWORD WINAPI ThreadProc( - __in LPVOID pvContext - ) - { - CWixStandardBootstrapperApplication* pThis = static_cast(pvContext);; - - HRESULT hr = S_OK; - IXMLDOMDocument *pixd = NULL; - IXMLDOMNode* pNode = NULL; - LPWSTR sczUpdateUrl = NULL; - DWORD64 qwSize = 0; - - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Checking for update."); - - // Load the update XML from a location url and parse it for an update. - // - // - // - // - // - - hr = XmlLoadDocumentFromFile(pThis->m_wzUpdateLocation, &pixd); - BalExitOnFailure(hr, "Failed to load version check XML document."); - - hr = XmlSelectSingleNode(pixd, L"/Setup/Upgrade", &pNode); - BalExitOnFailure(hr, "Failed to select upgrade node."); - - if (S_OK == hr) - { - hr = XmlGetAttributeEx(pNode, L"Url", &sczUpdateUrl); - BalExitOnFailure(hr, "Failed to get url attribute."); - - hr = XmlGetAttributeLargeNumber(pNode, L"Size", &qwSize); - } - - if (sczUpdateUrl && *sczUpdateUrl) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Update available, url: %ls; size: %I64u.", sczUpdateUrl, qwSize); - // Show upgrade on install and modify pages - if (pThis->m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == pThis->m_dwCurrentPage || - pThis->m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == pThis->m_dwCurrentPage) - { - pThis->m_pEngine->SetUpdate(NULL, sczUpdateUrl, qwSize, BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE, NULL, 0); - ThemeControlEnable(pThis->m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK, TRUE); - } - } - else - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "No update available."); - } - - LExit: - ReleaseObject(pixd); - ReleaseObject(pNode); - ReleaseStr(sczUpdateUrl); - - return 0; - } - - - // - // InitializeData - initializes all the package and prereq information. - // - HRESULT InitializeData() - { - HRESULT hr = S_OK; - LPWSTR sczModulePath = NULL; - IXMLDOMDocument *pixdManifest = NULL; - - hr = BalManifestLoad(m_hModule, &pixdManifest); - BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); - - hr = ParseOverridableVariablesFromXml(pixdManifest); - BalExitOnFailure(hr, "Failed to read overridable variables."); - - hr = ProcessCommandLine(&m_sczLanguage); - ExitOnFailure(hr, "Unknown commandline parameters."); - - hr = PathRelativeToModule(&sczModulePath, NULL, m_hModule); - BalExitOnFailure(hr, "Failed to get module path."); - - hr = LoadLocalization(sczModulePath, m_sczLanguage); - ExitOnFailure(hr, "Failed to load localization."); - - hr = LoadTheme(sczModulePath, m_sczLanguage); - ExitOnFailure(hr, "Failed to load theme."); - - hr = BalInfoParseFromXml(&m_Bundle, pixdManifest); - BalExitOnFailure(hr, "Failed to load bundle information."); - - hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, m_pWixLoc); - BalExitOnFailure(hr, "Failed to load conditions from XML."); - - LoadBootstrapperBAFunctions(); - - hr = ParseBootrapperApplicationDataFromXml(pixdManifest); - BalExitOnFailure(hr, "Failed to read bootstrapper application data."); - - LExit: - ReleaseObject(pixdManifest); - ReleaseStr(sczModulePath); - - return hr; - } - - - // - // ProcessCommandLine - process the provided command line arguments. - // - HRESULT ProcessCommandLine( - __inout LPWSTR* psczLanguage - ) - { - HRESULT hr = S_OK; - int argc = 0; - LPWSTR* argv = NULL; - LPWSTR sczVariableName = NULL; - LPWSTR sczVariableValue = NULL; - - if (m_command.wzCommandLine && *m_command.wzCommandLine) - { - argv = ::CommandLineToArgvW(m_command.wzCommandLine, &argc); - ExitOnNullWithLastError(argv, hr, "Failed to get command line."); - - for (int i = 0; i < argc; ++i) - { - if (argv[i][0] == L'-' || argv[i][0] == L'/') - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) - { - if (i + 1 >= argc) - { - hr = E_INVALIDARG; - BalExitOnFailure(hr, "Must specify a language."); - } - - ++i; - - hr = StrAllocString(psczLanguage, &argv[i][0], 0); - BalExitOnFailure(hr, "Failed to copy language."); - } - } - else if (m_sdOverridableVariables) - { - const wchar_t* pwc = wcschr(argv[i], L'='); - if (pwc) - { - hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); - BalExitOnFailure(hr, "Failed to copy variable name."); - - hr = DictKeyExists(m_sdOverridableVariables, sczVariableName); - if (E_NOTFOUND == hr) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName); - hr = S_OK; - continue; - } - ExitOnFailure(hr, "Failed to check the dictionary of overridable variables."); - - hr = StrAllocString(&sczVariableValue, ++pwc, 0); - BalExitOnFailure(hr, "Failed to copy variable value."); - - hr = m_pEngine->SetVariableString(sczVariableName, sczVariableValue); - BalExitOnFailure(hr, "Failed to set variable."); - } - else - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]); - } - } - } - } - - LExit: - if (argv) - { - ::LocalFree(argv); - } - - ReleaseStr(sczVariableName); - ReleaseStr(sczVariableValue); - - return hr; - } - - HRESULT LoadLocalization( - __in_z LPCWSTR wzModulePath, - __in_z_opt LPCWSTR wzLanguage - ) - { - HRESULT hr = S_OK; - LPWSTR sczLocPath = NULL; - LPCWSTR wzLocFileName = L"thm.wxl"; - - hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath); - BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath); - - hr = LocLoadFromFile(sczLocPath, &m_pWixLoc); - BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath); - - if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != m_pWixLoc->dwLangId) - { - ::SetThreadLocale(m_pWixLoc->dwLangId); - } - - hr = StrAllocString(&m_sczConfirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0); - ExitOnFailure(hr, "Failed to initialize confirm message loc identifier."); - - hr = LocLocalizeString(m_pWixLoc, &m_sczConfirmCloseMessage); - BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", m_sczConfirmCloseMessage); - - LExit: - ReleaseStr(sczLocPath); - - return hr; - } - - - HRESULT LoadTheme( - __in_z LPCWSTR wzModulePath, - __in_z_opt LPCWSTR wzLanguage - ) - { - HRESULT hr = S_OK; - LPWSTR sczThemePath = NULL; - LPCWSTR wzThemeFileName = L"thm.xml"; - LPWSTR sczCaption = NULL; - - hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath); - BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath); - - hr = ThemeLoadFromFile(sczThemePath, &m_pTheme); - BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath); - - hr = ThemeLocalize(m_pTheme, m_pWixLoc); - BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath); - - // Update the caption if there are any formatted strings in it. - hr = BalFormatString(m_pTheme->sczCaption, &sczCaption); - if (SUCCEEDED(hr)) - { - ThemeUpdateCaption(m_pTheme, sczCaption); - } - - LExit: - ReleaseStr(sczCaption); - ReleaseStr(sczThemePath); - - return hr; - } - - - HRESULT ParseOverridableVariablesFromXml( - __in IXMLDOMDocument* pixdManifest - ) - { - HRESULT hr = S_OK; - IXMLDOMNode* pNode = NULL; - IXMLDOMNodeList* pNodes = NULL; - DWORD cNodes = 0; - LPWSTR scz = NULL; - - // get the list of variables users can override on the command line - hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes); - if (S_FALSE == hr) - { - ExitFunction1(hr = S_OK); - } - ExitOnFailure(hr, "Failed to select overridable variable nodes."); - - hr = pNodes->get_length((long*)&cNodes); - ExitOnFailure(hr, "Failed to get overridable variable node count."); - - if (cNodes) - { - hr = DictCreateStringList(&m_sdOverridableVariables, 32, DICT_FLAG_NONE); - ExitOnFailure(hr, "Failed to create the string dictionary."); - - for (DWORD i = 0; i < cNodes; ++i) - { - hr = XmlNextElement(pNodes, &pNode, NULL); - ExitOnFailure(hr, "Failed to get next node."); - - // @Name - hr = XmlGetAttributeEx(pNode, L"Name", &scz); - ExitOnFailure(hr, "Failed to get @Name."); - - hr = DictAddKey(m_sdOverridableVariables, scz); - ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz); - - // prepare next iteration - ReleaseNullObject(pNode); - } - } - - LExit: - ReleaseObject(pNode); - ReleaseObject(pNodes); - ReleaseStr(scz); - return hr; - } - - - HRESULT ParsePrerequisiteInformationFromXml( - __in IXMLDOMDocument* pixdManifest - ) - { - HRESULT hr = S_OK; - IXMLDOMNode* pNode = NULL; - - hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqInformation", &pNode); - if (S_FALSE == hr) - { - hr = E_INVALIDARG; - } - BalExitOnFailure(hr, "BootstrapperApplication.xml manifest is missing prerequisite information."); - - hr = XmlGetAttributeEx(pNode, L"PackageId", &m_sczPrereqPackage); - BalExitOnFailure(hr, "Failed to get prerequisite package identifier."); - - hr = XmlGetAttributeEx(pNode, L"LicenseUrl", &m_sczLicenseUrl); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - BalExitOnFailure(hr, "Failed to get prerequisite license URL."); - - hr = XmlGetAttributeEx(pNode, L"LicenseFile", &m_sczLicenseFile); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - BalExitOnFailure(hr, "Failed to get prerequisite license file."); - - LExit: - ReleaseObject(pNode); - return hr; - } - - - HRESULT ParseBootrapperApplicationDataFromXml( - __in IXMLDOMDocument* pixdManifest - ) - { - HRESULT hr = S_OK; - IXMLDOMNode* pNode = NULL; - DWORD dwBool = 0; - - hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixExtbaInformation", &pNode); - if (S_FALSE == hr) - { - hr = E_INVALIDARG; - } - BalExitOnFailure(hr, "BootstrapperApplication.xml manifest is missing wixextba information."); - - hr = XmlGetAttributeEx(pNode, L"LicenseFile", &m_sczLicenseFile); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - BalExitOnFailure(hr, "Failed to get license file."); - - hr = XmlGetAttributeEx(pNode, L"LicenseUrl", &m_sczLicenseUrl); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - BalExitOnFailure(hr, "Failed to get license URL."); - - ReleaseObject(pNode); - - hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixExtbaOptions", &pNode); - if (S_FALSE == hr) - { - ExitFunction1(hr = S_OK); - } - BalExitOnFailure(hr, "Failed to read wixextba options from BootstrapperApplication.xml manifest."); - - hr = XmlGetAttributeNumber(pNode, L"SuppressOptionsUI", &dwBool); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (SUCCEEDED(hr)) - { - m_fSuppressOptionsUI = 0 < dwBool; - } - BalExitOnFailure(hr, "Failed to get SuppressOptionsUI value."); - - dwBool = 0; - hr = XmlGetAttributeNumber(pNode, L"SuppressDowngradeFailure", &dwBool); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (SUCCEEDED(hr)) - { - m_fSuppressDowngradeFailure = 0 < dwBool; - } - BalExitOnFailure(hr, "Failed to get SuppressDowngradeFailure value."); - - dwBool = 0; - hr = XmlGetAttributeNumber(pNode, L"SuppressRepair", &dwBool); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (SUCCEEDED(hr)) - { - m_fSuppressRepair = 0 < dwBool; - } - BalExitOnFailure(hr, "Failed to get SuppressRepair value."); - - hr = XmlGetAttributeNumber(pNode, L"ShowVersion", &dwBool); - if (E_NOTFOUND == hr) - { - hr = S_OK; - } - else if (SUCCEEDED(hr)) - { - m_fShowVersion = 0 < dwBool; - } - BalExitOnFailure(hr, "Failed to get ShowVersion value."); + // + // UiThreadProc - entrypoint for UI thread. + // + static DWORD WINAPI UiThreadProc( + __in LPVOID pvContext + ) + { + HRESULT hr = S_OK; + CWixStandardBootstrapperApplication* pThis = (CWixStandardBootstrapperApplication*)pvContext; + BOOL fComInitialized = FALSE; + BOOL fRet = FALSE; + MSG msg = { }; + + // Initialize COM and theme. + hr = ::CoInitialize(NULL); + BalExitOnFailure(hr, "Failed to initialize COM."); + fComInitialized = TRUE; + + hr = ThemeInitialize(pThis->m_hModule); + BalExitOnFailure(hr, "Failed to initialize theme manager."); + + hr = pThis->InitializeData(); + BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application."); + + // Create main window. + pThis->InitializeTaskbarButton(); + hr = pThis->CreateMainWindow(); + BalExitOnFailure(hr, "Failed to create main window."); + + if (FAILED(pThis->m_hrFinal)) + { + pThis->SetState(WIXSTDBA_STATE_FAILED, hr); + ::PostMessageW(pThis->m_hWnd, WM_WIXSTDBA_SHOW_FAILURE, 0, 0); + } + else + { + // Okay, we're ready for packages now. + pThis->SetState(WIXSTDBA_STATE_INITIALIZED, hr); + ::PostMessageW(pThis->m_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->m_command.action ? WM_WIXSTDBA_SHOW_HELP : WM_WIXSTDBA_DETECT_PACKAGES, 0, 0); + } + + // message pump + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + BalExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!ThemeHandleKeyboardMessage(pThis->m_pTheme, msg.hwnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + + // Succeeded thus far, check to see if anything went wrong while actually + // executing changes. + if (FAILED(pThis->m_hrFinal)) + { + hr = pThis->m_hrFinal; + } + else if (pThis->CheckCanceled()) + { + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); + } + + LExit: + // destroy main window + pThis->DestroyMainWindow(); + + // initiate engine shutdown + DWORD dwQuit = HRESULT_CODE(hr); + if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->m_restartResult) + { + dwQuit = ERROR_SUCCESS_REBOOT_INITIATED; + } + else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->m_restartResult) + { + dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED; + } + pThis->m_pEngine->Quit(dwQuit); + + ReleaseTheme(pThis->m_pTheme); + ThemeUninitialize(); + + // uninitialize COM + if (fComInitialized) + { + ::CoUninitialize(); + } + + return hr; + } + + + static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) + { + CWixStandardBootstrapperApplication* pThis = static_cast(pvContext);; + + HRESULT hr = S_OK; + IXMLDOMDocument *pixd = NULL; + IXMLDOMNode* pNode = NULL; + LPWSTR sczUpdateUrl = NULL; + DWORD64 qwSize = 0; + + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Checking for update."); + + // Load the update XML from a location url and parse it for an update. + // + // + // + // + // + + hr = XmlLoadDocumentFromFile(pThis->m_wzUpdateLocation, &pixd); + BalExitOnFailure(hr, "Failed to load version check XML document."); + + hr = XmlSelectSingleNode(pixd, L"/Setup/Upgrade", &pNode); + BalExitOnFailure(hr, "Failed to select upgrade node."); + + if (S_OK == hr) + { + hr = XmlGetAttributeEx(pNode, L"Url", &sczUpdateUrl); + BalExitOnFailure(hr, "Failed to get url attribute."); + + hr = XmlGetAttributeLargeNumber(pNode, L"Size", &qwSize); + } + + if (sczUpdateUrl && *sczUpdateUrl) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Update available, url: %ls; size: %I64u.", sczUpdateUrl, qwSize); + // Show upgrade on install and modify pages + if (pThis->m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == pThis->m_dwCurrentPage || + pThis->m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == pThis->m_dwCurrentPage) + { + pThis->m_pEngine->SetUpdate(NULL, sczUpdateUrl, qwSize, BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE, NULL, 0); + ThemeControlEnable(pThis->m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK, TRUE); + } + } + else + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "No update available."); + } + +LExit: + ReleaseObject(pixd); + ReleaseObject(pNode); + ReleaseStr(sczUpdateUrl); + + return 0; + } + + + // + // InitializeData - initializes all the package and prerequisite information. + // + HRESULT InitializeData() + { + HRESULT hr = S_OK; + LPWSTR sczModulePath = NULL; + IXMLDOMDocument *pixdManifest = NULL; + + hr = BalManifestLoad(m_hModule, &pixdManifest); + BalExitOnFailure(hr, "Failed to load bootstrapper application manifest."); + + hr = ParseOverridableVariablesFromXml(pixdManifest); + BalExitOnFailure(hr, "Failed to read overridable variables."); + + hr = ProcessCommandLine(&m_sczLanguage); + ExitOnFailure(hr, "Unknown commandline parameters."); + + hr = PathRelativeToModule(&sczModulePath, NULL, m_hModule); + BalExitOnFailure(hr, "Failed to get module path."); + + hr = LoadLocalization(sczModulePath, m_sczLanguage); + ExitOnFailure(hr, "Failed to load localization."); + + hr = LoadTheme(sczModulePath, m_sczLanguage); + ExitOnFailure(hr, "Failed to load theme."); + + hr = BalInfoParseFromXml(&m_Bundle, pixdManifest); + BalExitOnFailure(hr, "Failed to load bundle information."); + + hr = BalConditionsParseFromXml(&m_Conditions, pixdManifest, m_pWixLoc); + BalExitOnFailure(hr, "Failed to load conditions from XML."); + + hr = LoadBootstrapperBAFunctions(); + BalExitOnFailure(hr, "Failed to load bootstrapper functions."); + + GetBundleFileVersion(); + // don't fail if we couldn't get the version info; best-effort only + + if (m_fPrereq) + { + hr = ParsePrerequisiteInformationFromXml(pixdManifest); + BalExitOnFailure(hr, "Failed to read prerequisite information."); + } + else + { + hr = ParseBootrapperApplicationDataFromXml(pixdManifest); + BalExitOnFailure(hr, "Failed to read bootstrapper application data."); + } + + LExit: + ReleaseObject(pixdManifest); + ReleaseStr(sczModulePath); + + return hr; + } + + + // + // ProcessCommandLine - process the provided command line arguments. + // + HRESULT ProcessCommandLine( + __inout LPWSTR* psczLanguage + ) + { + HRESULT hr = S_OK; + int argc = 0; + LPWSTR* argv = NULL; + LPWSTR sczVariableName = NULL; + LPWSTR sczVariableValue = NULL; + + if (m_command.wzCommandLine && *m_command.wzCommandLine) + { + argv = ::CommandLineToArgvW(m_command.wzCommandLine, &argc); + ExitOnNullWithLastError(argv, hr, "Failed to get command line."); + + for (int i = 0; i < argc; ++i) + { + if (argv[i][0] == L'-' || argv[i][0] == L'/') + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) + { + if (i + 1 >= argc) + { + hr = E_INVALIDARG; + BalExitOnFailure(hr, "Must specify a language."); + } + + ++i; + + hr = StrAllocString(psczLanguage, &argv[i][0], 0); + BalExitOnFailure(hr, "Failed to copy language."); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"cache", -1)) + { + m_plannedAction = BOOTSTRAPPER_ACTION_CACHE; + } + } + else if (m_sdOverridableVariables) + { + const wchar_t* pwc = wcschr(argv[i], L'='); + if (pwc) + { + hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]); + BalExitOnFailure(hr, "Failed to copy variable name."); + + hr = DictKeyExists(m_sdOverridableVariables, sczVariableName); + if (E_NOTFOUND == hr) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName); + hr = S_OK; + continue; + } + ExitOnFailure(hr, "Failed to check the dictionary of overridable variables."); + + hr = StrAllocString(&sczVariableValue, ++pwc, 0); + BalExitOnFailure(hr, "Failed to copy variable value."); + + hr = m_pEngine->SetVariableString(sczVariableName, sczVariableValue); + BalExitOnFailure(hr, "Failed to set variable."); + } + else + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]); + } + } + } + } + + LExit: + if (argv) + { + ::LocalFree(argv); + } + + ReleaseStr(sczVariableName); + ReleaseStr(sczVariableValue); + + return hr; + } + + HRESULT LoadLocalization( + __in_z LPCWSTR wzModulePath, + __in_z_opt LPCWSTR wzLanguage + ) + { + HRESULT hr = S_OK; + LPWSTR sczLocPath = NULL; + LPWSTR sczFormatted = NULL; + LPCWSTR wzLocFileName = L"thm.wxl"; + + // Find and load .wxl file. + hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath); + BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath); + + hr = LocLoadFromFile(sczLocPath, &m_pWixLoc); + BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath); + + // Set WixStdBALanguageId to .wxl language id. + if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != m_pWixLoc->dwLangId) + { + ::SetThreadLocale(m_pWixLoc->dwLangId); + + hr = m_pEngine->SetVariableNumeric(WIXSTDBA_VARIABLE_LANGUAGE_ID, m_pWixLoc->dwLangId); + BalExitOnFailure(hr, "Failed to set WixStdBALanguageId variable."); + } + + // Load ConfirmCancelMessage. + hr = StrAllocString(&m_sczConfirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0); + ExitOnFailure(hr, "Failed to initialize confirm message loc identifier."); + + hr = LocLocalizeString(m_pWixLoc, &m_sczConfirmCloseMessage); + BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", m_sczConfirmCloseMessage); + + hr = BalFormatString(m_sczConfirmCloseMessage, &sczFormatted); + if (SUCCEEDED(hr)) + { + ReleaseStr(m_sczConfirmCloseMessage); + m_sczConfirmCloseMessage = sczFormatted; + sczFormatted = NULL; + } + + // For v3.9->v3.10 transition, duplicate new-to-v3.10 strings. + // Do not merge to WiX v4.0. + LOC_STRING* pLocString = NULL; + hr = LocGetString(m_pWixLoc, L"#(loc.SuccessInstallHeader)", &pLocString); + if (E_NOTFOUND == hr) + { + // Duplicate strings, best-effort only. + if (SUCCEEDED(LocGetString(m_pWixLoc, L"#(loc.SuccessHeader)", &pLocString))) + { + hr = LocAddString(m_pWixLoc, L"SuccessInstallHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for SuccessInstallHeader."); + + hr = LocAddString(m_pWixLoc, L"SuccessRepairHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for SuccessRepairHeader."); + + hr = LocAddString(m_pWixLoc, L"SuccessUninstallHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for SuccessUninstallHeader."); + } + + if (SUCCEEDED(LocGetString(m_pWixLoc, L"#(loc.FailureHeader)", &pLocString))) + { + hr = LocAddString(m_pWixLoc, L"FailureInstallHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for FailureInstallHeader."); + + hr = LocAddString(m_pWixLoc, L"FailureRepairHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for FailureRepairHeader."); + + hr = LocAddString(m_pWixLoc, L"FailureUninstallHeader", pLocString->wzText, pLocString->bOverridable); + ExitOnFailure(hr, "Failed to duplicate localization string for FailureUninstallHeader."); + } + } + + LExit: + ReleaseStr(sczFormatted); + ReleaseStr(sczLocPath); + + return hr; + } + + + HRESULT LoadTheme( + __in_z LPCWSTR wzModulePath, + __in_z_opt LPCWSTR wzLanguage + ) + { + HRESULT hr = S_OK; + LPWSTR sczThemePath = NULL; + LPCWSTR wzThemeFileName = L"thm.xml"; + LPWSTR sczCaption = NULL; + + hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath); + BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath); + + hr = ThemeLoadFromFile(sczThemePath, &m_pTheme); + BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath); + + hr = ThemeLocalize(m_pTheme, m_pWixLoc); + BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath); + + // Update the caption if there are any formatted strings in it. + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + hr = BalFormatString(m_pTheme->sczCaption, &sczCaption); + if (SUCCEEDED(hr)) + { + ThemeUpdateCaption(m_pTheme, sczCaption); + } + +LExit: + ReleaseStr(sczCaption); + ReleaseStr(sczThemePath); + + return hr; + } + + + HRESULT ParseOverridableVariablesFromXml( + __in IXMLDOMDocument* pixdManifest + ) + { + HRESULT hr = S_OK; + IXMLDOMNode* pNode = NULL; + IXMLDOMNodeList* pNodes = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + + // get the list of variables users can override on the command line + hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select overridable variable nodes."); + + hr = pNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get overridable variable node count."); + + if (cNodes) + { + hr = DictCreateStringList(&m_sdOverridableVariables, 32, DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create the string dictionary."); + + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pNodes, &pNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @Name + hr = XmlGetAttributeEx(pNode, L"Name", &scz); + ExitOnFailure(hr, "Failed to get @Name."); + + hr = DictAddKey(m_sdOverridableVariables, scz); + ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz); + + // prepare next iteration + ReleaseNullObject(pNode); + } + } + + LExit: + ReleaseObject(pNode); + ReleaseObject(pNodes); + ReleaseStr(scz); + return hr; + } + + + HRESULT ParsePrerequisiteInformationFromXml( + __in IXMLDOMDocument* pixdManifest + ) + { + HRESULT hr = S_OK; + IXMLDOMNode* pNode = NULL; + IXMLDOMNodeList* pNodes = NULL; + DWORD cNodes = 0; + LPWSTR scz = NULL; + WIXSTDBA_PREREQ_PACKAGE* pPrereqPackage = NULL; + BAL_INFO_PACKAGE* pPackage = NULL; + + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqInformation", &pNode); + if (S_FALSE == hr) + { + hr = E_INVALIDARG; + } + BalExitOnFailure(hr, "BootstrapperApplication.xml manifest is missing prerequisite information."); + + hr = XmlGetAttributeEx(pNode, L"PackageId", &m_sczPrereqPackage); + BalExitOnFailure(hr, "Failed to get prerequisite package identifier."); + + hr = XmlGetAttributeEx(pNode, L"LicenseUrl", &m_sczLicenseUrl); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + BalExitOnFailure(hr, "Failed to get prerequisite license URL."); + + hr = XmlGetAttributeEx(pNode, L"LicenseFile", &m_sczLicenseFile); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + BalExitOnFailure(hr, "Failed to get prerequisite license file."); + + // get the list of prerequisite support packages + hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/MbaPrerequisiteSupportPackage", &pNodes); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + ExitOnFailure(hr, "Failed to select prerequisite support package nodes."); + + hr = pNodes->get_length((long*)&cNodes); + ExitOnFailure(hr, "Failed to get prerequisite support package node count."); + + m_cPrereqPackages = cNodes + 1; + m_rgPrereqPackages = static_cast(MemAlloc(sizeof(WIXSTDBA_PREREQ_PACKAGE) * m_cPrereqPackages, TRUE)); + + hr = DictCreateWithEmbeddedKey(&m_shPrereqSupportPackages, m_cPrereqPackages, reinterpret_cast(&m_rgPrereqPackages), offsetof(WIXSTDBA_PREREQ_PACKAGE, sczPackageId), DICT_FLAG_NONE); + ExitOnFailure(hr, "Failed to create the prerequisite package dictionary."); + + pPrereqPackage = m_rgPrereqPackages; + pPrereqPackage->sczPackageId = m_sczPrereqPackage; + pPrereqPackage->fAlwaysInstall = TRUE; + hr = DictAddValue(m_shPrereqSupportPackages, pPrereqPackage); + ExitOnFailure1(hr, "Failed to add \"%ls\" to the prerequisite package dictionary.", pPrereqPackage->sczPackageId); + + for (DWORD i = 0; i < cNodes; ++i) + { + hr = XmlNextElement(pNodes, &pNode, NULL); + ExitOnFailure(hr, "Failed to get next node."); + + // @PackageId + hr = XmlGetAttributeEx(pNode, L"PackageId", &scz); + ExitOnFailure(hr, "Failed to get @PackageId."); + + hr = DictGetValue(m_shPrereqSupportPackages, scz, reinterpret_cast(&pPrereqPackage)); + if (SUCCEEDED(hr)) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, scz, -1, m_sczPrereqPackage, -1)) + { + pPrereqPackage->fAlwaysInstall = FALSE; + } + ReleaseNullObject(pNode); + continue; + } + else if (E_NOTFOUND != hr) + { + ExitOnFailure1(hr, "Failed to check if \"%ls\" was in the prerequisite package dictionary.", scz); + } + + hr = BalInfoFindPackageById(&m_Bundle.packages, scz, &pPackage); + if (SUCCEEDED(hr)) + { + pPrereqPackage = &m_rgPrereqPackages[i + 1]; + pPrereqPackage->sczPackageId = pPackage->sczId; + hr = DictAddValue(m_shPrereqSupportPackages, pPrereqPackage); + ExitOnFailure1(hr, "Failed to add \"%ls\" to the prerequisite package dictionary.", pPrereqPackage->sczPackageId); + } + else + { + BalLogError(hr, "Failed to get info about \"%ls\" from BootstrapperApplicationData.", scz); + } + + // prepare next iteration + ReleaseNullObject(pNode); + } + + LExit: + ReleaseObject(pNode); + ReleaseObject(pNodes); + ReleaseStr(scz); + return hr; + } + + + HRESULT ParseBootrapperApplicationDataFromXml( + __in IXMLDOMDocument* pixdManifest + ) + { + HRESULT hr = S_OK; + IXMLDOMNode* pNode = NULL; + DWORD dwBool = 0; + + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixExtbaInformation", &pNode); + if (S_FALSE == hr) + { + hr = E_INVALIDARG; + } + BalExitOnFailure(hr, "BootstrapperApplication.xml manifest is missing wixextba information."); + + hr = XmlGetAttributeEx(pNode, L"LicenseFile", &m_sczLicenseFile); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + BalExitOnFailure(hr, "Failed to get license file."); + + hr = XmlGetAttributeEx(pNode, L"LicenseUrl", &m_sczLicenseUrl); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + BalExitOnFailure(hr, "Failed to get license URL."); + + ReleaseObject(pNode); + + hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixExtbaOptions", &pNode); + if (S_FALSE == hr) + { + ExitFunction1(hr = S_OK); + } + BalExitOnFailure(hr, "Failed to read wixextba options from BootstrapperApplication.xml manifest."); + + hr = XmlGetAttributeNumber(pNode, L"SuppressOptionsUI", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fSuppressOptionsUI = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get SuppressOptionsUI value."); + + dwBool = 0; + hr = XmlGetAttributeNumber(pNode, L"SuppressDowngradeFailure", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fSuppressDowngradeFailure = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get SuppressDowngradeFailure value."); + + dwBool = 0; + hr = XmlGetAttributeNumber(pNode, L"SuppressRepair", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fSuppressRepair = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get SuppressRepair value."); + + hr = XmlGetAttributeNumber(pNode, L"ShowVersion", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fShowVersion = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get ShowVersion value."); + + hr = XmlGetAttributeNumber(pNode, L"SupportCacheOnly", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fSupportCacheOnly = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get SupportCacheOnly value."); + + hr = XmlGetAttributeNumber(pNode, L"ShowFilesInUse", &dwBool); + if (E_NOTFOUND == hr) + { + hr = S_OK; + } + else if (SUCCEEDED(hr)) + { + m_fShowFilesInUse = 0 < dwBool; + } + BalExitOnFailure(hr, "Failed to get ShowFilesInUse value."); dwBool = 0; hr = XmlGetAttributeNumber(pNode, L"LaunchPassive", &dwBool); @@ -1375,548 +1702,706 @@ private: // privates } BalExitOnFailure(hr, "Failed to get LaunchQuiet value."); - LExit: - ReleaseObject(pNode); - return hr; - } + LExit: + ReleaseObject(pNode); + return hr; + } + + HRESULT GetPrereqPackage( + __in_z LPCWSTR wzPackageId, + __out WIXSTDBA_PREREQ_PACKAGE** ppPrereqPackage, + __out BAL_INFO_PACKAGE** ppPackage + ) + { + HRESULT hr = E_NOTFOUND; + WIXSTDBA_PREREQ_PACKAGE* pPrereqPackage = NULL; + BAL_INFO_PACKAGE* pPackage = NULL; + + Assert(wzPackageId && *wzPackageId); + Assert(ppPackage); + Assert(ppPrereqPackage); + + if (m_shPrereqSupportPackages) + { + hr = DictGetValue(m_shPrereqSupportPackages, wzPackageId, reinterpret_cast(&pPrereqPackage)); + if (E_NOTFOUND != hr) + { + ExitOnFailure(hr, "Failed to check the dictionary of prerequisite packages."); + + // Ignore error. + BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage); + } + } + + if (pPrereqPackage) + { + *ppPrereqPackage = pPrereqPackage; + *ppPackage = pPackage; + } + LExit: + return hr; + } - // - // CreateMainWindow - creates the main install window. - // - HRESULT CreateMainWindow() - { - HRESULT hr = S_OK; - HICON hIcon = reinterpret_cast(m_pTheme->hIcon); - WNDCLASSW wc = {}; - DWORD dwWindowStyle = 0; - int x = CW_USEDEFAULT; - int y = CW_USEDEFAULT; - POINT ptCursor = {}; - HMONITOR hMonitor = NULL; - MONITORINFO mi = {}; + // + // Get the file version of the bootstrapper and record in bootstrapper log file + // + HRESULT GetBundleFileVersion() + { + HRESULT hr = S_OK; + ULARGE_INTEGER uliVersion = { }; + LPWSTR sczCurrentPath = NULL; - // If the theme did not provide an icon, try using the icon from the bundle engine. - if (!hIcon) - { - HMODULE hBootstrapperEngine = ::GetModuleHandleW(NULL); - if (hBootstrapperEngine) - { - hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1)); - } - } + hr = PathForCurrentProcess(&sczCurrentPath, NULL); + BalExitOnFailure(hr, "Failed to get bundle path."); - // Register the window class and create the window. - wc.lpfnWndProc = CWixStandardBootstrapperApplication::WndProc; - wc.hInstance = m_hModule; - wc.hIcon = hIcon; - wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); - wc.hbrBackground = m_pTheme->rgFonts[m_pTheme->dwFontId].hBackground; - wc.lpszMenuName = NULL; - wc.lpszClassName = WIXSTDBA_WINDOW_CLASS; - if (!::RegisterClassW(&wc)) - { - ExitWithLastError(hr, "Failed to register window."); - } + hr = FileVersion(sczCurrentPath, &uliVersion.HighPart, &uliVersion.LowPart); + BalExitOnFailure(hr, "Failed to get bundle file version."); - m_fRegistered = TRUE; + hr = m_pEngine->SetVariableVersion(WIXSTDBA_VARIABLE_BUNDLE_FILE_VERSION, uliVersion.QuadPart); + BalExitOnFailure(hr, "Failed to set WixBundleFileVersion variable."); - // Calculate the window style based on the theme style and command display value. - dwWindowStyle = m_pTheme->dwStyle; - if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display) - { - dwWindowStyle &= ~WS_VISIBLE; - } + LExit: + ReleaseStr(sczCurrentPath); - // Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden) - if (::IsWindow(m_command.hwndSplashScreen)) - { - dwWindowStyle &= ~WS_VISIBLE; - } - - // Center the window on the monitor with the mouse. - if (::GetCursorPos(&ptCursor)) - { - hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); - if (hMonitor) - { - mi.cbSize = sizeof(mi); - if (::GetMonitorInfoW(hMonitor, &mi)) - { - x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - m_pTheme->nWidth) / 2; - y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - m_pTheme->nHeight) / 2; - } - } - } - - m_hWnd = ::CreateWindowExW(0, wc.lpszClassName, m_pTheme->sczCaption, dwWindowStyle, x, y, m_pTheme->nWidth, m_pTheme->nHeight, HWND_DESKTOP, NULL, m_hModule, this); - ExitOnNullWithLastError(m_hWnd, hr, "Failed to create window."); - - hr = S_OK; - - LExit: - return hr; - } + return hr; + } - // - // InitializeTaskbarButton - initializes taskbar button for progress. - // - void InitializeTaskbarButton() - { - HRESULT hr = S_OK; + // + // CreateMainWindow - creates the main install window. + // + HRESULT CreateMainWindow() + { + HRESULT hr = S_OK; + HICON hIcon = reinterpret_cast(m_pTheme->hIcon); + WNDCLASSW wc = { }; + DWORD dwWindowStyle = 0; + int x = CW_USEDEFAULT; + int y = CW_USEDEFAULT; + POINT ptCursor = { }; + HMONITOR hMonitor = NULL; + MONITORINFO mi = { }; - hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast(&m_pTaskbarList)); - if (REGDB_E_CLASSNOTREG == hr) // not supported before Windows 7 - { - ExitFunction1(hr = S_OK); - } - BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing."); + // If the theme did not provide an icon, try using the icon from the bundle engine. + if (!hIcon) + { + HMODULE hBootstrapperEngine = ::GetModuleHandleW(NULL); + if (hBootstrapperEngine) + { + hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1)); + } + } - m_uTaskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated"); - BalExitOnNullWithLastError(m_uTaskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing."); + // Register the window class and create the window. + wc.lpfnWndProc = CWixStandardBootstrapperApplication::WndProc; + wc.hInstance = m_hModule; + wc.hIcon = hIcon; + wc.hCursor = ::LoadCursorW(NULL, (LPCWSTR)IDC_ARROW); + wc.hbrBackground = m_pTheme->rgFonts[m_pTheme->dwFontId].hBackground; + wc.lpszMenuName = NULL; + wc.lpszClassName = WIXSTDBA_WINDOW_CLASS; + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } - LExit: - return; - } + m_fRegistered = TRUE; - // - // DestroyMainWindow - clean up all the window registration. - // - void DestroyMainWindow() - { - if (::IsWindow(m_hWnd)) - { - ::DestroyWindow(m_hWnd); - m_hWnd = NULL; - m_fTaskbarButtonOK = FALSE; - } + // Calculate the window style based on the theme style and command display value. + dwWindowStyle = m_pTheme->dwStyle; + if (BOOTSTRAPPER_DISPLAY_NONE >= m_command.display) + { + dwWindowStyle &= ~WS_VISIBLE; + } - if (m_fRegistered) - { - ::UnregisterClassW(WIXSTDBA_WINDOW_CLASS, m_hModule); - m_fRegistered = FALSE; - } - } + // Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden) + if (::IsWindow(m_command.hwndSplashScreen)) + { + dwWindowStyle &= ~WS_VISIBLE; + } + + // Center the window on the monitor with the mouse. + if (::GetCursorPos(&ptCursor)) + { + hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST); + if (hMonitor) + { + mi.cbSize = sizeof(mi); + if (::GetMonitorInfoW(hMonitor, &mi)) + { + x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - m_pTheme->nWidth) / 2; + y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - m_pTheme->nHeight) / 2; + } + } + } + + m_hWnd = ::CreateWindowExW(0, wc.lpszClassName, m_pTheme->sczCaption, dwWindowStyle, x, y, m_pTheme->nWidth, m_pTheme->nHeight, HWND_DESKTOP, NULL, m_hModule, this); + ExitOnNullWithLastError(m_hWnd, hr, "Failed to create window."); + + hr = S_OK; + + LExit: + return hr; + } - // - // WndProc - standard windows message handler. - // - static LRESULT CALLBACK WndProc( - __in HWND hWnd, - __in UINT uMsg, - __in WPARAM wParam, - __in LPARAM lParam - ) - { + // + // InitializeTaskbarButton - initializes taskbar button for progress. + // + void InitializeTaskbarButton() + { + HRESULT hr = S_OK; + + hr = ::CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast(&m_pTaskbarList)); + if (REGDB_E_CLASSNOTREG == hr) // not supported before Windows 7 + { + ExitFunction1(hr = S_OK); + } + BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing."); + + m_uTaskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated"); + BalExitOnNullWithLastError(m_uTaskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing."); + + LExit: + return; + } + + // + // DestroyMainWindow - clean up all the window registration. + // + void DestroyMainWindow() + { + if (::IsWindow(m_hWnd)) + { + ::DestroyWindow(m_hWnd); + m_hWnd = NULL; + m_fTaskbarButtonOK = FALSE; + } + + if (m_fRegistered) + { + ::UnregisterClassW(WIXSTDBA_WINDOW_CLASS, m_hModule); + m_fRegistered = FALSE; + } + } + + + // + // WndProc - standard windows message handler. + // + static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) + { #pragma warning(suppress:4312) - CWixStandardBootstrapperApplication* pBA = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); + CWixStandardBootstrapperApplication* pBA = reinterpret_cast(::GetWindowLongPtrW(hWnd, GWLP_USERDATA)); - switch (uMsg) - { - case WM_NCCREATE: - { - LPCREATESTRUCT lpcs = reinterpret_cast(lParam); - pBA = reinterpret_cast(lpcs->lpCreateParams); + switch (uMsg) + { + case WM_NCCREATE: + { + LPCREATESTRUCT lpcs = reinterpret_cast(lParam); + pBA = reinterpret_cast(lpcs->lpCreateParams); #pragma warning(suppress:4244) - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pBA)); - } - break; - - case WM_NCDESTROY: - { - LRESULT lres = ThemeDefWindowProc(pBA ? pBA->m_pTheme : NULL, hWnd, uMsg, wParam, lParam); - ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); - return lres; - } - - case WM_CREATE: - if (!pBA->OnCreate(hWnd)) - { - return -1; - } - break; - - case WM_QUERYENDSESSION: - return IDCANCEL != pBA->OnSystemShutdown(static_cast(lParam), IDCANCEL); - - case WM_CLOSE: - // If the user chose not to close, do *not* let the default window proc handle the message. - if (!pBA->OnClose()) - { - return 0; - } - break; - - case WM_DESTROY: - ::PostQuitMessage(0); - break; - - case WM_WIXSTDBA_SHOW_HELP: - pBA->OnShowHelp(); - return 0; - - case WM_WIXSTDBA_DETECT_PACKAGES: - pBA->OnDetect(); - return 0; - - case WM_WIXSTDBA_PLAN_PACKAGES: - pBA->OnPlan(static_cast(lParam)); - return 0; - - case WM_WIXSTDBA_APPLY_PACKAGES: - pBA->OnApply(); - return 0; - - case WM_WIXSTDBA_CHANGE_STATE: - pBA->OnChangeState(static_cast(lParam)); - return 0; - - case WM_COMMAND: - switch (LOWORD(wParam)) - { - case WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX: - pBA->OnClickAcceptCheckbox(); - return 0; - - case WIXSTDBA_CONTROL_OPTIONS_BUTTON: - pBA->OnClickOptionsButton(); - return 0; - - case WIXSTDBA_CONTROL_BROWSE_BUTTON: - pBA->OnClickOptionsBrowseButton(WIXSTDBA_CONTROL_BROWSE_BUTTON); - return 0; - - case WIXSTDBA_CONTROL_BROWSE_BUTTON2: - pBA->OnClickOptionsBrowseButton(WIXSTDBA_CONTROL_BROWSE_BUTTON2); - return 0; - - case WIXSTDBA_CONTROL_OK_BUTTON: - pBA->OnClickOptionsOkButton(); - return 0; - - case WIXSTDBA_CONTROL_CANCEL_BUTTON: - pBA->OnClickOptionsCancelButton(); - return 0; - - case WIXSTDBA_CONTROL_INSTALL_BUTTON: - pBA->OnClickInstallButton(); - return 0; - - case WIXSTDBA_CONTROL_REPAIR_BUTTON: - pBA->OnClickRepairButton(); - return 0; - - case WIXSTDBA_CONTROL_UNINSTALL_BUTTON: - pBA->OnClickUninstallButton(); - return 0; - - case WIXSTDBA_CONTROL_LAUNCH_BUTTON: - pBA->OnClickLaunchButton(); - return 0; - - case WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON: - pBA->OnClickRestartButton(); - return 0; - - case WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON: __fallthrough; - - case WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON: __fallthrough; - case WIXSTDBA_CONTROL_CLOSE_BUTTON: - pBA->OnClickCloseButton(); - return 0; - } - break; - - case WM_NOTIFY: - if (lParam) - { - LPNMHDR pnmhdr = reinterpret_cast(lParam); - switch (pnmhdr->code) - { - case NM_CLICK: __fallthrough; - case NM_RETURN: - switch (static_cast(pnmhdr->idFrom)) - { - case WIXSTDBA_CONTROL_EULA_LINK: - pBA->OnClickEulaLink(); - return 1; - case WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK: - pBA->OnClickLogFileLink(); - return 1; - case WIXSTDBA_CONTROL_UPGRADE_LINK: - pBA->OnClickUpgradeLink(); - return 1; - } - } - } - break; - } - - if (pBA && pBA->m_pTaskbarList && uMsg == pBA->m_uTaskbarButtonCreatedMessage) - { - pBA->m_fTaskbarButtonOK = TRUE; - return 0; - } - - return ThemeDefWindowProc(pBA ? pBA->m_pTheme : NULL, hWnd, uMsg, wParam, lParam); - } - - - // - // OnCreate - finishes loading the theme. - // - BOOL OnCreate( - __in HWND hWnd - ) - { - HRESULT hr = S_OK; - LPWSTR sczText = NULL; - LPWSTR sczLicenseFormatted = NULL; - LPWSTR sczLicensePath = NULL; - LPWSTR sczLicenseDirectory = NULL; - LPWSTR sczLicenseFilename = NULL; - - hr = ThemeLoadControls(m_pTheme, hWnd, vrgInitControls, countof(vrgInitControls)); - BalExitOnFailure(hr, "Failed to load theme controls."); - - C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames)); - C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames)); - - ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds)); - - // Initialize the text on all "application" (non-page) controls. - for (DWORD i = 0; i < m_pTheme->cControls; ++i) - { - THEME_CONTROL* pControl = m_pTheme->rgControls + i; - if (!pControl->wPageId && pControl->sczText && *pControl->sczText) - { - HRESULT hrFormat = BalFormatString(pControl->sczText, &sczText); - if (SUCCEEDED(hrFormat)) - { - ThemeSetTextControl(m_pTheme, pControl->wId, sczText); - } - } - } - - // Load the RTF EULA control with text if the control exists. - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT)) - { - hr = (m_sczLicenseFile && *m_sczLicenseFile) ? S_OK : E_INVALIDDATA; - if (SUCCEEDED(hr)) - { - hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0); - if (SUCCEEDED(hr)) - { - hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted); - if (SUCCEEDED(hr)) - { - hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted); - if (SUCCEEDED(hr)) - { - hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule); - if (SUCCEEDED(hr)) - { - hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); - if (SUCCEEDED(hr)) - { - hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0); - if (SUCCEEDED(hr)) - { - hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath); - if (SUCCEEDED(hr)) - { - hr = ThemeLoadRichEditFromFile(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT, sczLicensePath, m_hModule); - } - } - } - } - } - } - } - } - - if (FAILED(hr)) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile); - hr = S_OK; - } - } - - LExit: - ReleaseStr(sczLicenseFilename); - ReleaseStr(sczLicenseDirectory); - ReleaseStr(sczLicensePath); - ReleaseStr(sczLicenseFormatted); - ReleaseStr(sczText); - - return SUCCEEDED(hr); - } - - - // - // OnShowHelp - display the help page. - // - void OnShowHelp() - { - SetState(WIXSTDBA_STATE_HELP, S_OK); - - // If the UI should be visible, display it now and hide the splash screen - if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) - { - ::ShowWindow(m_pTheme->hwndParent, SW_SHOW); - } - - m_pEngine->CloseSplashScreen(); - - return; - } - - - // - // OnDetect - start the processing of packages. - // - void OnDetect() - { - HRESULT hr = S_OK; - - if (m_pBAFunction) - { - hr = m_pBAFunction->OnDetect(); - BalExitOnFailure(hr, "Failed calling detect BA function."); - } - - SetState(WIXSTDBA_STATE_DETECTING, hr); - - // If the UI should be visible, display it now and hide the splash screen - if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) - { - ::ShowWindow(m_pTheme->hwndParent, SW_SHOW); - } - - m_pEngine->CloseSplashScreen(); - - // Tell the core we're ready for the packages to be processed now. - hr = m_pEngine->Detect(); - BalExitOnFailure(hr, "Failed to start detecting chain."); - - LExit: - if (FAILED(hr)) - { - SetState(WIXSTDBA_STATE_DETECTING, hr); - } - - return; - } - - - // - // OnPlan - plan the detected changes. - // - void OnPlan( - __in BOOTSTRAPPER_ACTION action - ) - { - HRESULT hr = S_OK; - - m_plannedAction = action; - - // If we are going to apply a downgrade, bail. - if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < action) - { - if (m_fSuppressDowngradeFailure) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing..."); - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); - BalExitOnFailure(hr, "Cannot install a product when a newer version is installed."); - } - } - - SetState(WIXSTDBA_STATE_PLANNING, hr); - - if (m_pBAFunction) - { - m_pBAFunction->OnPlan(); - } - - hr = m_pEngine->Plan(action); - BalExitOnFailure(hr, "Failed to start planning packages."); - - LExit: - if (FAILED(hr)) - { - SetState(WIXSTDBA_STATE_PLANNING, hr); - } - - return; - } - - - // - // OnApply - apply the packages. - // - void OnApply() - { - HRESULT hr = S_OK; - - SetState(WIXSTDBA_STATE_APPLYING, hr); - SetProgressState(hr); - SetTaskbarButtonProgress(0); - - hr = m_pEngine->Apply(m_hWnd); - BalExitOnFailure(hr, "Failed to start applying packages."); - - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, TRUE); // ensure the cancel button is enabled before starting. - - LExit: - if (FAILED(hr)) - { - SetState(WIXSTDBA_STATE_APPLYING, hr); - } - - return; - } - - - // - // OnChangeState - change state. - // - void OnChangeState( - __in WIXSTDBA_STATE state - ) - { - WIXSTDBA_STATE stateOld = m_state; - DWORD dwOldPageId = 0; - DWORD dwNewPageId = 0; - LPWSTR sczText = NULL; - LPWSTR sczUnformattedText = NULL; - LPWSTR sczControlState = NULL; - LPWSTR sczControlName = NULL; - - m_state = state; - - // If our install is at the end (success or failure) and we're not showing full UI or - // we successfully installed the prerequisite then exit (prompt for restart if required). - if ((WIXSTDBA_STATE_APPLIED <= m_state && BOOTSTRAPPER_DISPLAY_FULL > m_command.display) || - (WIXSTDBA_STATE_APPLIED == m_state && m_fPrereq)) - { - // If a restart was required but we were not automatically allowed to - // accept the reboot then do the prompt. - if (m_fRestartRequired && !m_fAllowRestart) - { - StrAllocFromError(&sczUnformattedText, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), NULL); - - int nResult = ::MessageBoxW(m_hWnd, sczUnformattedText ? sczUnformattedText : L"The requested operation is successful. Changes will not be effective until the system is rebooted.", m_pTheme->sczCaption, MB_ICONEXCLAMATION | MB_OKCANCEL); - m_fAllowRestart = (IDOK == nResult); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast(pBA)); + } + break; + + case WM_NCDESTROY: + { + LRESULT lres = ThemeDefWindowProc(pBA ? pBA->m_pTheme : NULL, hWnd, uMsg, wParam, lParam); + ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0); + ::PostQuitMessage(0); + return lres; + } + + case WM_CREATE: + if (!pBA->OnCreate(hWnd)) + { + return -1; + } + break; + + case WM_QUERYENDSESSION: + return IDCANCEL != pBA->OnSystemShutdown(static_cast(lParam), IDCANCEL); + + case WM_CLOSE: + // If the user chose not to close, do *not* let the default window proc handle the message. + if (!pBA->OnClose()) + { + return 0; + } + break; + + case WM_WIXSTDBA_SHOW_HELP: + pBA->OnShowHelp(); + return 0; + + case WM_WIXSTDBA_DETECT_PACKAGES: + pBA->OnDetect(); + return 0; + + case WM_WIXSTDBA_PLAN_PACKAGES: + pBA->OnPlan(static_cast(lParam)); + return 0; + + case WM_WIXSTDBA_APPLY_PACKAGES: + pBA->OnApply(); + return 0; + + case WM_WIXSTDBA_CHANGE_STATE: + pBA->OnChangeState(static_cast(lParam)); + return 0; + + case WM_WIXSTDBA_SHOW_STATE_MODAL: + return pBA->OnShowStateModal(static_cast(lParam)); + + case WM_WIXSTDBA_SHOW_FAILURE: + pBA->OnShowFailure(); + return 0; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX: + pBA->OnClickAcceptCheckbox(); + return 0; + + case WIXSTDBA_CONTROL_OPTIONS_BUTTON: + pBA->OnClickOptionsButton(); + return 0; + + case WIXSTDBA_CONTROL_BROWSE_BUTTON: + pBA->OnClickOptionsBrowseButton(WIXSTDBA_CONTROL_BROWSE_BUTTON); + return 0; + + case WIXSTDBA_CONTROL_BROWSE_BUTTON2: + pBA->OnClickOptionsBrowseButton(WIXSTDBA_CONTROL_BROWSE_BUTTON2); + return 0; + + case WIXSTDBA_CONTROL_OK_BUTTON: + pBA->OnClickOptionsOkButton(); + return 0; + + case WIXSTDBA_CONTROL_CANCEL_BUTTON: + pBA->OnClickOptionsCancelButton(); + return 0; + + case WIXSTDBA_CONTROL_INSTALL_BUTTON: + pBA->OnClickInstallButton(); + return 0; + + case WIXSTDBA_CONTROL_REPAIR_BUTTON: + pBA->OnClickRepairButton(); + return 0; + + case WIXSTDBA_CONTROL_UNINSTALL_BUTTON: + pBA->OnClickUninstallButton(); + return 0; + + case WIXSTDBA_CONTROL_LAUNCH_BUTTON: + pBA->OnClickLaunchButton(); + return 0; + + case WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON: + pBA->OnClickRestartButton(); + return 0; + + //Files in use + case WIXSTDBA_CONTROL_FILESINUSE_OK_BUTTON: + pBA->OnClickFilesInUseOkButton(); + return 0; + + case WIXSTDBA_CONTROL_HELP_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_WELCOME_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_MODIFY_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_SUCCESS_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_FAILURE_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_FILESINUSE_CANCEL_BUTTON: __fallthrough; + case WIXSTDBA_CONTROL_CLOSE_BUTTON: + pBA->OnClickCloseButton(); + return 0; + } + break; + + case WM_NOTIFY: + if (lParam) + { + LPNMHDR pnmhdr = reinterpret_cast(lParam); + switch (pnmhdr->code) + { + case NM_CLICK: __fallthrough; + case NM_RETURN: + switch (static_cast(pnmhdr->idFrom)) + { + case WIXSTDBA_CONTROL_EULA_LINK: + pBA->OnClickEulaLink(); + return 1; + case WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK: + pBA->OnClickLogFileLink(); + return 1; + case WIXSTDBA_CONTROL_UPGRADE_LINK: + pBA->OnClickUpgradeLink(); + return 1; + } + } + } + break; + } + + if (pBA && pBA->m_pTaskbarList && uMsg == pBA->m_uTaskbarButtonCreatedMessage) + { + pBA->m_fTaskbarButtonOK = TRUE; + return 0; + } + + return ThemeDefWindowProc(pBA ? pBA->m_pTheme : NULL, hWnd, uMsg, wParam, lParam); + } + + + // + // OnCreate - finishes loading the theme. + // + BOOL OnCreate( + __in HWND hWnd + ) + { + HRESULT hr = S_OK; + LPWSTR sczText = NULL; + LPWSTR sczLicenseFormatted = NULL; + LPWSTR sczLicensePath = NULL; + LPWSTR sczLicenseDirectory = NULL; + LPWSTR sczLicenseFilename = NULL; + + hr = ThemeLoadControls(m_pTheme, hWnd, vrgInitControls, countof(vrgInitControls)); + BalExitOnFailure(hr, "Failed to load theme controls."); + + C_ASSERT(COUNT_WIXSTDBA_PAGE == countof(vrgwzPageNames)); + C_ASSERT(countof(m_rgdwPageIds) == countof(vrgwzPageNames)); + + ThemeGetPageIds(m_pTheme, vrgwzPageNames, m_rgdwPageIds, countof(m_rgdwPageIds)); + + // Initialize the text on all "application" (non-page) controls. + for (DWORD i = 0; i < m_pTheme->cControls; ++i) + { + THEME_CONTROL* pControl = m_pTheme->rgControls + i; + if (!pControl->wPageId && pControl->sczText && *pControl->sczText) + { + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + HRESULT hrFormat = BalFormatString(pControl->sczText, &sczText); + if (SUCCEEDED(hrFormat)) + { + ThemeSetTextControl(m_pTheme, pControl->wId, sczText); + } + } + } + + // Load the RTF EULA control with text if the control exists. + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT)) + { + hr = (m_sczLicenseFile && *m_sczLicenseFile) ? S_OK : E_INVALIDDATA; + if (SUCCEEDED(hr)) + { + hr = StrAllocString(&sczLicenseFormatted, m_sczLicenseFile, 0); + if (SUCCEEDED(hr)) + { + hr = LocLocalizeString(m_pWixLoc, &sczLicenseFormatted); + if (SUCCEEDED(hr)) + { + // Assume there is no hidden variables to be formatted + // so don't worry about securely freeing it. + hr = BalFormatString(sczLicenseFormatted, &sczLicenseFormatted); + if (SUCCEEDED(hr)) + { + hr = PathRelativeToModule(&sczLicensePath, sczLicenseFormatted, m_hModule); + if (SUCCEEDED(hr)) + { + hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); + if (SUCCEEDED(hr)) + { + hr = StrAllocString(&sczLicenseFilename, PathFile(sczLicenseFormatted), 0); + if (SUCCEEDED(hr)) + { + hr = LocProbeForFile(sczLicenseDirectory, sczLicenseFilename, m_sczLanguage, &sczLicensePath); + if (SUCCEEDED(hr)) + { + hr = ThemeLoadRichEditFromFile(m_pTheme, WIXSTDBA_CONTROL_EULA_RICHEDIT, sczLicensePath, m_hModule); + } + } + } + } + } + } + } + } + + if (FAILED(hr)) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load file into license richedit control from path '%ls' manifest value: %ls", sczLicensePath, m_sczLicenseFile); + hr = S_OK; + } + } + + LExit: + ReleaseStr(sczLicenseFilename); + ReleaseStr(sczLicenseDirectory); + ReleaseStr(sczLicensePath); + ReleaseStr(sczLicenseFormatted); + ReleaseStr(sczText); + + return SUCCEEDED(hr); + } + + + // + // OnShowFailure - display the failure page. + // + void OnShowFailure() + { + SetState(WIXSTDBA_STATE_FAILED, S_OK); + + // If the UI should be visible, display it now and hide the splash screen + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) + { + ::ShowWindow(m_pTheme->hwndParent, SW_SHOW); + } + + m_pEngine->CloseSplashScreen(); + + return; + } + + + // + // OnShowHelp - display the help page. + // + void OnShowHelp() + { + SetState(WIXSTDBA_STATE_HELP, S_OK); + + // If the UI should be visible, display it now and hide the splash screen + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) + { + ::ShowWindow(m_pTheme->hwndParent, SW_SHOW); + } + + m_pEngine->CloseSplashScreen(); + + return; + } + + + // + // OnShowStateModal - display the given state modal. + // + int OnShowStateModal(WIXSTDBA_STATE state) + { + MSG msg = {}; + int nResult = IDERROR; + BOOL fDone = FALSE; + TBPFLAG flag = TBPF_PAUSED; + + // The state before showing our page. + WIXSTDBA_STATE stateBeforeModal = m_state; + + // Set taskbar to paused. + SetTaskbarButtonState(flag); + SetState(state, S_OK); + + ++m_cModalPages; + + // Inner message loop + while (!fDone && !IsCanceled()) + { + ::WaitMessage(); + + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (WM_WIXSTDBA_CLOSE_STATE_MODAL == msg.message) + { + fDone = TRUE; + nResult = static_cast(msg.lParam); + + break; + } + else if (WM_QUIT == msg.message) + { + // Exit during modal page. + fDone = TRUE; + + //Forward quit message to main message loop + ::PostQuitMessage(0); + + break; + } + else if (!ThemeHandleKeyboardMessage(m_pTheme, msg.hwnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + } + + --m_cModalPages; + + //Restore taskbar state + flag = TBPF_NORMAL; + SetTaskbarButtonState(flag); + + //Restore previous state + SetState(stateBeforeModal, S_OK); + + return IsCanceled() ? IDCANCEL : nResult; + } + + + // + // OnDetect - start the processing of packages. + // + void OnDetect() + { + HRESULT hr = S_OK; + + if (m_pBAFunction) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect BA function"); + hr = m_pBAFunction->OnDetect(); + BalExitOnFailure(hr, "Failed calling detect BA function."); + } + + SetState(WIXSTDBA_STATE_DETECTING, hr); + + // If the UI should be visible, display it now and hide the splash screen + if (BOOTSTRAPPER_DISPLAY_NONE < m_command.display) + { + ::ShowWindow(m_pTheme->hwndParent, SW_SHOW); + } + + m_pEngine->CloseSplashScreen(); + + // Tell the core we're ready for the packages to be processed now. + hr = m_pEngine->Detect(); + BalExitOnFailure(hr, "Failed to start detecting chain."); + + LExit: + if (FAILED(hr)) + { + SetState(WIXSTDBA_STATE_DETECTING, hr); + } + + return; + } + + + // + // OnPlan - plan the detected changes. + // + void OnPlan( + __in BOOTSTRAPPER_ACTION action + ) + { + HRESULT hr = S_OK; + + m_plannedAction = action; + + // If we are going to apply a downgrade, bail. + if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < action) + { + if (m_fSuppressDowngradeFailure) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing..."); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); + BalExitOnFailure(hr, "Cannot install a product when a newer version is installed."); + } + } + + SetState(WIXSTDBA_STATE_PLANNING, hr); + + if (m_pBAFunction) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan BA function"); + m_pBAFunction->OnPlan(); + } + + hr = m_pEngine->Plan(action); + BalExitOnFailure(hr, "Failed to start planning packages."); + + LExit: + if (FAILED(hr)) + { + SetState(WIXSTDBA_STATE_PLANNING, hr); + } + + return; + } + + + // + // OnApply - apply the packages. + // + void OnApply() + { + HRESULT hr = S_OK; + + SetState(WIXSTDBA_STATE_APPLYING, hr); + SetProgressState(hr); + SetTaskbarButtonProgress(0); + + hr = m_pEngine->Apply(m_hWnd); + BalExitOnFailure(hr, "Failed to start applying packages."); + + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, TRUE); // ensure the cancel button is enabled before starting. + + LExit: + if (FAILED(hr)) + { + SetState(WIXSTDBA_STATE_APPLYING, hr); + } + + return; + } + + + // + // OnChangeState - change state. + // + void OnChangeState( + __in WIXSTDBA_STATE state + ) + { + WIXSTDBA_STATE stateOld = m_state; + DWORD dwOldPageId = 0; + DWORD dwNewPageId = 0; + LPWSTR sczText = NULL; + LPWSTR sczUnformattedText = NULL; + LPWSTR sczControlState = NULL; + LPWSTR sczControlName = NULL; + + m_state = state; + + // If our install is at the end (success or failure) and we're not showing full UI or + // we successfully installed the prerequisite then exit (prompt for restart if required). + if ((WIXSTDBA_STATE_APPLIED <= m_state && BOOTSTRAPPER_DISPLAY_FULL > m_command.display) || + (WIXSTDBA_STATE_APPLIED == m_state && m_fPrereq)) + { + // If a restart was required but we were not automatically allowed to + // accept the reboot then do the prompt. + if (m_fRestartRequired && !m_fAllowRestart) + { + StrAllocFromError(&sczUnformattedText, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), NULL); + + int nResult = ::MessageBoxW(m_hWnd, sczUnformattedText ? sczUnformattedText : L"The requested operation is successful. Changes will not be effective until the system is rebooted.", m_pTheme->sczCaption, MB_ICONEXCLAMATION | MB_OKCANCEL); + m_fAllowRestart = (IDOK == nResult); } if ((BOOTSTRAPPER_DISPLAY_PASSIVE == m_command.display && m_fLaunchPassive) || (m_command.display == BOOTSTRAPPER_DISPLAY_NONE && m_fLaunchQuiet)) @@ -1927,1049 +2412,1333 @@ private: // privates } // Quietly exit. - ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); - } - else // try to change the pages. - { - DeterminePageId(stateOld, &dwOldPageId); - DeterminePageId(m_state, &dwNewPageId); - - if (dwOldPageId != dwNewPageId) - { - // Enable disable controls per-page. - if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) // on the "Install" page, ensure the install button is enabled/disabled correctly. - { - LONGLONG llElevated = 0; - if (m_Bundle.fPerMachine) - { - BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated); - } - ThemeControlElevates(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, (m_Bundle.fPerMachine && !llElevated)); - - // If the EULA control exists, show it only if a license URL is provided as well. - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_LINK)) - { - BOOL fEulaLink = (m_sczLicenseUrl && *m_sczLicenseUrl); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_EULA_LINK, fEulaLink); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, fEulaLink); - } - - BOOL fAcceptedLicense = !ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX) || !ThemeControlEnabled(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX) || ThemeIsControlChecked(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, fAcceptedLicense); - - // If there is an "Options" page, the "Options" button exists, and it hasn't been suppressed, then enable the button. - BOOL fOptionsEnabled = m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_OPTIONS_BUTTON) && !m_fSuppressOptionsUI; - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_OPTIONS_BUTTON, fOptionsEnabled); - - // Show/Hide the version label if it exists. - if (m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_VERSION_LABEL) && !m_fShowVersion) - { - ThemeShowControl(m_pTheme, WIXSTDBA_CONTROL_VERSION_LABEL, SW_HIDE); - } - } - else if (m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) - { - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_REPAIR_BUTTON, !m_fSuppressRepair); - } - else if (m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] == dwNewPageId) - { - HRESULT hr = BalGetStringVariable(WIXSTDBA_VARIABLE_INSTALL_FOLDER, &sczUnformattedText); - if (SUCCEEDED(hr)) - { - BalFormatString(sczUnformattedText, &sczText); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, sczText); - } - - hr = BalGetStringVariable(WIXSTDBA_VARIABLE_INSTALL_FOLDER2, &sczUnformattedText); - if (SUCCEEDED(hr)) - { - BalFormatString(sczUnformattedText, &sczText); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, sczText); - } - } - else if (m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS] == dwNewPageId) // on the "Success" page, check if the restart or launch button should be enabled. - { - BOOL fShowRestartButton = FALSE; - BOOL fLaunchTargetExists = FALSE; - if (m_fRestartRequired) - { - if (BOOTSTRAPPER_RESTART_PROMPT == m_command.restart) - { - fShowRestartButton = TRUE; - } - } - else if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_LAUNCH_BUTTON)) - { - fLaunchTargetExists = BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH); - } - - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_LAUNCH_BUTTON, fLaunchTargetExists && BOOTSTRAPPER_ACTION_UNINSTALL < m_plannedAction); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, fShowRestartButton); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, fShowRestartButton); - } - else if (m_rgdwPageIds[WIXSTDBA_PAGE_FAILURE] == dwNewPageId) // on the "Failure" page, show error message and check if the restart button should be enabled. - { - BOOL fShowLogLink = (m_Bundle.sczLogVariable && *m_Bundle.sczLogVariable); // if there is a log file variable then we'll assume the log file exists. - BOOL fShowErrorMessage = FALSE; - BOOL fShowRestartButton = FALSE; - - if (FAILED(m_hrFinal)) - { - // If we know the failure message, use that. - if (m_sczFailedMessage && *m_sczFailedMessage) - { - StrAllocString(&sczUnformattedText, m_sczFailedMessage, 0); - } - else // try to get the error message from the error code. - { - StrAllocFromError(&sczUnformattedText, m_hrFinal, NULL); - if (!sczUnformattedText || !*sczUnformattedText) - { - StrAllocFromError(&sczUnformattedText, E_FAIL, NULL); - } - } - - StrAllocFormatted(&sczText, L"0x%08x - %ls", m_hrFinal, sczUnformattedText); - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, sczText); - fShowErrorMessage = TRUE; - } - - if (m_fRestartRequired) - { - if (BOOTSTRAPPER_RESTART_PROMPT == m_command.restart) - { - fShowRestartButton = TRUE; - } - } - - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, fShowLogLink); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, fShowErrorMessage); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, fShowRestartButton); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, fShowRestartButton); - } - - // Hide the upgrade link - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK)) - { - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK, FALSE); - } - - // Process each control for special handling in the new page. - THEME_PAGE* pPage = ThemeGetPage(m_pTheme, dwNewPageId); - if (pPage) - { - for (DWORD i = 0; i < pPage->cControlIndices; ++i) - { - THEME_CONTROL* pControl = m_pTheme->rgControls + pPage->rgdwControlIndices[i]; - - // If we are on the install, options or modify pages and this is a named control, try to set its default state. - if ((m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId || - m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] == dwNewPageId || - m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) && - pControl->sczName && *pControl->sczName) - { - // If this is a checkbox control, try to set its default state to the state of a matching named Burn variable. - if (THEME_CONTROL_TYPE_CHECKBOX == pControl->type && WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX != pControl->wId) - { - LONGLONG llValue = 0; - HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue); - - ThemeSendControlMessage(m_pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); - } - - // If this is a button control with the BS_AUTORADIOBUTTON style, try to set its default - // state to the state of a matching named Burn variable. - if (THEME_CONTROL_TYPE_BUTTON == pControl->type && (BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & pControl->dwStyle))) - { - LONGLONG llValue = 0; - HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue); - - // If the control value isn't set then disable it. - if (!SUCCEEDED(hr)) - { - ThemeControlEnable(m_pTheme, pControl->wId, FALSE); - } - else - { - ThemeSendControlMessage(m_pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); - } - } - - // Hide or disable controls based on the control name with 'State' appended - HRESULT hr = StrAllocFormatted(&sczControlName, L"%lsState", pControl->sczName); - if (SUCCEEDED(hr)) - { - hr = BalGetStringVariable(sczControlName, &sczControlState); - if (SUCCEEDED(hr) && sczControlState && *sczControlState) - { - if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, sczControlState, -1, L"disable", -1)) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Disable control %ls", pControl->sczName); - ThemeControlEnable(m_pTheme, pControl->wId, FALSE); - } - else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, sczControlState, -1, L"hide", -1)) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hide control %ls", pControl->sczName); - // TODO: This doesn't work - ThemeShowControl(m_pTheme, pControl->wId, SW_HIDE); - } - } - } - } - - // Format the text in each of the new page's controls (if they have any text). - if (pControl->sczText && *pControl->sczText) - { - HRESULT hr = BalFormatString(pControl->sczText, &sczText); - if (SUCCEEDED(hr)) - { - ThemeSetTextControl(m_pTheme, pControl->wId, sczText); - } - } - } - } - - ThemeShowPage(m_pTheme, dwOldPageId, SW_HIDE); - ThemeShowPage(m_pTheme, dwNewPageId, SW_SHOW); - - // Remember current page - m_dwCurrentPage = dwNewPageId; - - // On the install page set the focus to the install button or the next enabled control if install is disabled - if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) - { - HWND hwndFocus = ::GetDlgItem(m_pTheme->hwndParent, WIXSTDBA_CONTROL_INSTALL_BUTTON); - if (hwndFocus && !ThemeControlEnabled(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON)) - { - hwndFocus = ::GetNextDlgTabItem(m_pTheme->hwndParent, hwndFocus, FALSE); - } - - if (hwndFocus) - { - ::SetFocus(hwndFocus); - } - } - } - } - - ReleaseStr(sczText); - ReleaseStr(sczUnformattedText); - ReleaseStr(sczControlState); - ReleaseStr(sczControlName); - } - - - // - // OnClose - called when the window is trying to be closed. - // - BOOL OnClose() - { - BOOL fClose = FALSE; - - // If we've already succeeded or failed or showing the help page, just close (prompts are annoying if the bootstrapper is done). - if (WIXSTDBA_STATE_APPLIED <= m_state || WIXSTDBA_STATE_HELP == m_state) - { - fClose = TRUE; - } - else // prompt the user or force the cancel if there is no UI. - { - fClose = PromptCancel(m_hWnd, BOOTSTRAPPER_DISPLAY_FULL != m_command.display, m_sczConfirmCloseMessage ? m_sczConfirmCloseMessage : L"Are you sure you want to cancel?", m_pTheme->sczCaption); - } - - // If we're doing progress then we never close, we just cancel to let rollback occur. - if (WIXSTDBA_STATE_APPLYING <= m_state && WIXSTDBA_STATE_APPLIED > m_state) - { - // If we canceled disable cancel button since clicking it again is silly. - if (fClose) - { - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, FALSE); - } - - fClose = FALSE; - } - - return fClose; - } - - - // - // OnClickAcceptCheckbox - allow the install to continue. - // - void OnClickAcceptCheckbox() - { - BOOL fAcceptedLicense = ThemeIsControlChecked(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX); - ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, fAcceptedLicense); - } - - - // - // OnClickOptionsButton - show the options page. - // - void OnClickOptionsButton() - { - SavePageSettings(WIXSTDBA_PAGE_INSTALL); - m_stateBeforeOptions = m_state; - SetState(WIXSTDBA_STATE_OPTIONS, S_OK); - } - - - // - // OnClickOptionsBrowseButton - browse for install folder on the options page. - // - void OnClickOptionsBrowseButton(DWORD dwControl) - { - WCHAR wzPath[MAX_PATH] = {}; - BROWSEINFOW browseInfo = {}; - PIDLIST_ABSOLUTE pidl = NULL; - PIDLIST_ABSOLUTE pidlRoot = NULL; - - ::SHGetFolderLocation(m_hWnd, CSIDL_DRIVES, NULL, 0, &pidlRoot); - - browseInfo.hwndOwner = m_hWnd; - browseInfo.pszDisplayName = wzPath; - browseInfo.lpszTitle = m_pTheme->sczCaption; - browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; - browseInfo.pidlRoot = pidlRoot; - pidl = ::SHBrowseForFolderW(&browseInfo); - if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) - { - switch (dwControl) - { - case WIXSTDBA_CONTROL_BROWSE_BUTTON: - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, wzPath); - break; - - case WIXSTDBA_CONTROL_BROWSE_BUTTON2: - ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, wzPath); - break; - } - } - - if (pidl) - { - ::CoTaskMemFree(pidl); - } - - return; - } - - // - // OnClickOptionsOkButton - accept the changes made by the options page. - // - void OnClickOptionsOkButton() - { - HRESULT hr = S_OK; - LPWSTR sczPath = NULL; - - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX)) - { - hr = ThemeGetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, &sczPath); - ExitOnFailure(hr, "Failed to get text from folder edit box."); - - // TODO: verify the path is valid. - - hr = m_pEngine->SetVariableString(WIXSTDBA_VARIABLE_INSTALL_FOLDER, sczPath); - ExitOnFailure(hr, "Failed to set the install folder."); - } - - if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2)) - { - hr = ThemeGetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, &sczPath); - ExitOnFailure(hr, "Failed to get text from folder edit box2."); - - // TODO: verify the path is valid. - - hr = m_pEngine->SetVariableString(WIXSTDBA_VARIABLE_INSTALL_FOLDER2, sczPath); - ExitOnFailure(hr, "Failed to set the install folder2."); - } - - SavePageSettings(WIXSTDBA_PAGE_OPTIONS); - - LExit: - SetState(m_stateBeforeOptions, S_OK); - return; - } - - - // - // OnClickOptionsCancelButton - discard the changes made by the options page. - // - void OnClickOptionsCancelButton() - { - SetState(m_stateBeforeOptions, S_OK); - } - - - // - // OnClickInstallButton - start the install by planning the packages. - // - void OnClickInstallButton() - { - SavePageSettings(WIXSTDBA_PAGE_INSTALL); - - this->OnPlan(BOOTSTRAPPER_ACTION_INSTALL); - } - - - // - // OnClickRepairButton - start the repair. - // - void OnClickRepairButton() - { - this->OnPlan(BOOTSTRAPPER_ACTION_REPAIR); - } - - - // - // OnClickUninstallButton - start the uninstall. - // - void OnClickUninstallButton() - { - this->OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL); - } - - - // - // OnClickCloseButton - close the application. - // - void OnClickCloseButton() - { - ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); - } - - - // - // OnClickEulaLink - show the end user license agreement. - // - void OnClickEulaLink() - { - HRESULT hr = S_OK; - LPWSTR sczLicenseUrl = NULL; - LPWSTR sczLicensePath = NULL; - LPWSTR sczLicenseDirectory = NULL; - URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; - - hr = StrAllocString(&sczLicenseUrl, m_sczLicenseUrl, 0); - BalExitOnFailure1(hr, "Failed to copy license URL: %ls", m_sczLicenseUrl); - - hr = LocLocalizeString(m_pWixLoc, &sczLicenseUrl); - BalExitOnFailure1(hr, "Failed to localize license URL: %ls", m_sczLicenseUrl); - - hr = BalFormatString(sczLicenseUrl, &sczLicenseUrl); - BalExitOnFailure1(hr, "Failed to get formatted license URL: %ls", m_sczLicenseUrl); - - hr = UriProtocol(sczLicenseUrl, &protocol); - if (FAILED(hr) || URI_PROTOCOL_UNKNOWN == protocol) - { - // Probe for localised license file - hr = PathRelativeToModule(&sczLicensePath, sczLicenseUrl, m_hModule); - if (SUCCEEDED(hr)) - { - hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); - if (SUCCEEDED(hr)) - { - hr = LocProbeForFile(sczLicenseDirectory, PathFile(sczLicenseUrl), m_sczLanguage, &sczLicensePath); - } - } - } - - hr = ShelExec(sczLicensePath ? sczLicensePath : sczLicenseUrl, NULL, L"open", NULL, SW_SHOWDEFAULT, m_hWnd, NULL); - BalExitOnFailure(hr, "Failed to launch URL to EULA."); - - LExit: - ReleaseStr(sczLicensePath); - ReleaseStr(sczLicenseUrl); - ReleaseStr(sczLicenseDirectory); - - return; - } - - - // - // OnClickUpgradeLink - download the upgrade. - // - void OnClickUpgradeLink() - { - this->OnPlan(BOOTSTRAPPER_ACTION_UPDATE_REPLACE); - - m_fUpdating = TRUE; - - return; - } - - - // - // OnClickLaunchButton - launch the app from the success page. - // - void OnClickLaunchButton() - { - HRESULT hr = S_OK; - LPWSTR sczUnformattedLaunchTarget = NULL; - LPWSTR sczLaunchTarget = NULL; - LPWSTR sczUnformattedArguments = NULL; - LPWSTR sczArguments = NULL; - int nCmdShow = SW_SHOWNORMAL; - - hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH, &sczUnformattedLaunchTarget); - BalExitOnFailure1(hr, "Failed to get launch target variable '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH); - - hr = BalFormatString(sczUnformattedLaunchTarget, &sczLaunchTarget); - BalExitOnFailure1(hr, "Failed to format launch target variable: %ls", sczUnformattedLaunchTarget); - - if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS)) - { - hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS, &sczUnformattedArguments); - BalExitOnFailure1(hr, "Failed to get launch arguments '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS); - - hr = BalFormatString(sczUnformattedArguments, &sczArguments); - BalExitOnFailure1(hr, "Failed to format launch arguments variable: %ls", sczUnformattedArguments); - } - - if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_HIDDEN)) - { - nCmdShow = SW_HIDE; - } - - hr = ShelExec(sczLaunchTarget, sczArguments, L"open", NULL, nCmdShow, m_hWnd, NULL); - BalExitOnFailure1(hr, "Failed to launch target: %ls", sczLaunchTarget); - - ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); - - LExit: - ReleaseStr(sczArguments); - ReleaseStr(sczUnformattedArguments); - ReleaseStr(sczLaunchTarget); - ReleaseStr(sczUnformattedLaunchTarget); - - return; - } - - - // - // OnClickRestartButton - allows the restart and closes the app. - // - void OnClickRestartButton() - { - AssertSz(m_fRestartRequired, "Restart must be requested to be able to click on the restart button."); - - m_fAllowRestart = TRUE; - ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); - - return; - } - - - // - // OnClickLogFileLink - show the log file. - // - void OnClickLogFileLink() - { - HRESULT hr = S_OK; - LPWSTR sczLogFile = NULL; - - hr = BalGetStringVariable(m_Bundle.sczLogVariable, &sczLogFile); - BalExitOnFailure1(hr, "Failed to get log file variable '%ls'.", m_Bundle.sczLogVariable); - - hr = ShelExec(L"notepad.exe", sczLogFile, L"open", NULL, SW_SHOWDEFAULT, m_hWnd, NULL); - BalExitOnFailure1(hr, "Failed to open log file target: %ls", sczLogFile); - - LExit: - ReleaseStr(sczLogFile); - - return; - } - - - // - // SetState - // - void SetState( - __in WIXSTDBA_STATE state, - __in HRESULT hrStatus - ) - { - if (FAILED(hrStatus)) - { - m_hrFinal = hrStatus; - } - - if (FAILED(m_hrFinal)) - { - state = WIXSTDBA_STATE_FAILED; - } - - if (WIXSTDBA_STATE_OPTIONS == state || m_state < state) - { - ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, state); - } - } - - - void DeterminePageId( - __in WIXSTDBA_STATE state, - __out DWORD* pdwPageId - ) - { - if (BOOTSTRAPPER_DISPLAY_PASSIVE == m_command.display) - { - switch (state) - { - case WIXSTDBA_STATE_INITIALIZED: - *pdwPageId = BOOTSTRAPPER_ACTION_HELP == m_command.action ? m_rgdwPageIds[WIXSTDBA_PAGE_HELP] : m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; - break; - - case WIXSTDBA_STATE_HELP: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_HELP]; - break; - - case WIXSTDBA_STATE_DETECTING: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_LOADING] ? m_rgdwPageIds[WIXSTDBA_PAGE_LOADING] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] ? m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; - break; - - case WIXSTDBA_STATE_DETECTED: __fallthrough; - case WIXSTDBA_STATE_PLANNING: __fallthrough; - case WIXSTDBA_STATE_PLANNED: __fallthrough; - case WIXSTDBA_STATE_APPLYING: __fallthrough; - case WIXSTDBA_STATE_CACHING: __fallthrough; - case WIXSTDBA_STATE_CACHED: __fallthrough; - case WIXSTDBA_STATE_EXECUTING: __fallthrough; - case WIXSTDBA_STATE_EXECUTED: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] ? m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; - break; - - default: - *pdwPageId = 0; - break; - } - } - else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) - { - switch (state) - { - case WIXSTDBA_STATE_INITIALIZING: - *pdwPageId = 0; - break; - - case WIXSTDBA_STATE_INITIALIZED: - *pdwPageId = BOOTSTRAPPER_ACTION_HELP == m_command.action ? m_rgdwPageIds[WIXSTDBA_PAGE_HELP] : m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; - break; - - case WIXSTDBA_STATE_HELP: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_HELP]; - break; - - case WIXSTDBA_STATE_DETECTING: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; - break; - - case WIXSTDBA_STATE_DETECTED: - switch (m_command.action) - { - case BOOTSTRAPPER_ACTION_INSTALL: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL]; - break; - - case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; - case BOOTSTRAPPER_ACTION_REPAIR: __fallthrough; - case BOOTSTRAPPER_ACTION_UNINSTALL: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY]; - break; - } - break; - - case WIXSTDBA_STATE_OPTIONS: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS]; - break; - - case WIXSTDBA_STATE_PLANNING: __fallthrough; - case WIXSTDBA_STATE_PLANNED: __fallthrough; - case WIXSTDBA_STATE_APPLYING: __fallthrough; - case WIXSTDBA_STATE_CACHING: __fallthrough; - case WIXSTDBA_STATE_CACHED: __fallthrough; - case WIXSTDBA_STATE_EXECUTING: __fallthrough; - case WIXSTDBA_STATE_EXECUTED: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; - break; - - case WIXSTDBA_STATE_APPLIED: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS]; - break; - - case WIXSTDBA_STATE_FAILED: - *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_FAILURE]; - break; - } - } - } - - - HRESULT EvaluateConditions() - { - HRESULT hr = S_OK; - BOOL fResult = FALSE; - - for (DWORD i = 0; i < m_Conditions.cConditions; ++i) - { - BAL_CONDITION* pCondition = m_Conditions.rgConditions + i; - - hr = BalConditionEvaluate(pCondition, m_pEngine, &fResult, &m_sczFailedMessage); - BalExitOnFailure(hr, "Failed to evaluate condition."); - - if (!fResult) - { - hr = E_WIXSTDBA_CONDITION_FAILED; - BalExitOnFailure1(hr, "Bundle condition evaluated to false: %ls", pCondition->sczCondition); - } - } - - ReleaseNullStr(m_sczFailedMessage); - - LExit: - return hr; - } - - - void SetTaskbarButtonProgress( - __in DWORD dwOverallPercentage - ) - { - HRESULT hr = S_OK; - - if (m_fTaskbarButtonOK) - { - hr = m_pTaskbarList->SetProgressValue(m_hWnd, dwOverallPercentage, 100UL); - BalExitOnFailure1(hr, "Failed to set taskbar button progress to: %d%%.", dwOverallPercentage); - } - - LExit: - return; - } - - - void SetTaskbarButtonState( - __in TBPFLAG tbpFlags - ) - { - HRESULT hr = S_OK; - - if (m_fTaskbarButtonOK) - { - hr = m_pTaskbarList->SetProgressState(m_hWnd, tbpFlags); - BalExitOnFailure1(hr, "Failed to set taskbar button state.", tbpFlags); - } - - LExit: - return; - } - - - void SetProgressState( - __in HRESULT hrStatus - ) - { - TBPFLAG flag = TBPF_NORMAL; - - if (IsCanceled() || HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hrStatus) - { - flag = TBPF_PAUSED; - } - else if (IsRollingBack() || FAILED(hrStatus)) - { - flag = TBPF_ERROR; - } - - SetTaskbarButtonState(flag); - } - - - HRESULT LoadBootstrapperBAFunctions() - { - HRESULT hr = S_OK; - LPWSTR sczBafPath = NULL; - - hr = PathRelativeToModule(&sczBafPath, L"bafunctions.dll", m_hModule); - BalExitOnFailure(hr, "Failed to get path to BA function DLL."); + ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); + } + else // try to change the pages. + { + DeterminePageId(stateOld, &dwOldPageId); + DeterminePageId(m_state, &dwNewPageId); + + if (dwOldPageId != dwNewPageId) + { + // Enable disable controls per-page. + if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) // on the "Install" page, ensure the install button is enabled/disabled correctly. + { + LONGLONG llElevated = 0; + if (m_Bundle.fPerMachine) + { + BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated); + } + ThemeControlElevates(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, (m_Bundle.fPerMachine && !llElevated)); + + // If the EULA control exists, show it only if a license URL is provided as well. + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_LINK)) + { + BOOL fEulaLink = (m_sczLicenseUrl && *m_sczLicenseUrl); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_EULA_LINK, fEulaLink); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX, fEulaLink); + } + + BOOL fAcceptedLicense = !ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX) || !ThemeControlEnabled(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX) || ThemeIsControlChecked(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, fAcceptedLicense); + + // If there is an "Options" page, the "Options" button exists, and it hasn't been suppressed, then enable the button. + BOOL fOptionsEnabled = m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_OPTIONS_BUTTON) && !m_fSuppressOptionsUI; + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_OPTIONS_BUTTON, fOptionsEnabled); + + // Show/Hide the version label if it exists. + if (m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_VERSION_LABEL) && !m_fShowVersion) + { + ThemeShowControl(m_pTheme, WIXSTDBA_CONTROL_VERSION_LABEL, SW_HIDE); + } + } + else if (m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_REPAIR_BUTTON, !m_fSuppressRepair); + } + else if (m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] == dwNewPageId) + { + HRESULT hr = BalGetStringVariable(WIXSTDBA_VARIABLE_INSTALL_FOLDER, &sczUnformattedText); + if (SUCCEEDED(hr)) + { + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + BalFormatString(sczUnformattedText, &sczText); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, sczText); + } + + hr = BalGetStringVariable(WIXSTDBA_VARIABLE_INSTALL_FOLDER2, &sczUnformattedText); + if (SUCCEEDED(hr)) + { + BalFormatString(sczUnformattedText, &sczText); + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, sczText); + } + } + else if (m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS] == dwNewPageId) // on the "Success" page, check if the restart or launch button should be enabled. + { + BOOL fShowRestartButton = FALSE; + BOOL fLaunchTargetExists = FALSE; + if (m_fRestartRequired) + { + if (BOOTSTRAPPER_RESTART_PROMPT == m_command.restart) + { + fShowRestartButton = TRUE; + } + } + else if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_LAUNCH_BUTTON)) + { + fLaunchTargetExists = BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH); + } + + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_LAUNCH_BUTTON, fLaunchTargetExists && BOOTSTRAPPER_ACTION_UNINSTALL < m_plannedAction); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_RESTART_TEXT, fShowRestartButton); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_RESTART_BUTTON, fShowRestartButton); + + if ((BOOTSTRAPPER_ACTION_INSTALL == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_INSTALL_HEADER)) || + (BOOTSTRAPPER_ACTION_UNINSTALL == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_UNINSTALL_HEADER)) || + (BOOTSTRAPPER_ACTION_REPAIR == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_REPAIR_HEADER))) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_HEADER, FALSE); + } + else + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_HEADER, TRUE); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_INSTALL_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_INSTALL_HEADER, BOOTSTRAPPER_ACTION_INSTALL == m_plannedAction); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_UNINSTALL_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_UNINSTALL_HEADER, BOOTSTRAPPER_ACTION_UNINSTALL == m_plannedAction); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_REPAIR_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_SUCCESS_REPAIR_HEADER, BOOTSTRAPPER_ACTION_REPAIR == m_plannedAction); + } + } + else if (m_rgdwPageIds[WIXSTDBA_PAGE_FAILURE] == dwNewPageId) // on the "Failure" page, show error message and check if the restart button should be enabled. + { + BOOL fShowLogLink = (m_Bundle.sczLogVariable && *m_Bundle.sczLogVariable); // if there is a log file variable then we'll assume the log file exists. + BOOL fShowErrorMessage = FALSE; + BOOL fShowRestartButton = FALSE; + + if (FAILED(m_hrFinal)) + { + // If we know the failure message, use that. + if (m_sczFailedMessage && *m_sczFailedMessage) + { + StrAllocString(&sczUnformattedText, m_sczFailedMessage, 0); + } + else if (E_MBAHOST_NET452_ON_WIN7RTM == m_hrFinal) + { + HRESULT hr = StrAllocString(&sczUnformattedText, L"#(loc.NET452WIN7RTMErrorMessage)", 0); + if (FAILED(hr)) + { + BalLogError(hr, "Failed to initialize NET452WIN7RTMErrorMessage loc identifier."); + } + else + { + hr = LocLocalizeString(m_pWixLoc, &sczUnformattedText); + if (FAILED(hr)) + { + BalLogError(hr, "Failed to localize NET452WIN7RTMErrorMessage: %ls", sczUnformattedText); + ReleaseNullStr(sczUnformattedText); + } + } + } + else // try to get the error message from the error code. + { + StrAllocFromError(&sczUnformattedText, m_hrFinal, NULL); + if (!sczUnformattedText || !*sczUnformattedText) + { + StrAllocFromError(&sczUnformattedText, E_FAIL, NULL); + } + } + + if (E_WIXSTDBA_CONDITION_FAILED == m_hrFinal) + { + if (sczUnformattedText) + { + StrAllocString(&sczText, sczUnformattedText, 0); + } + } + else if (E_MBAHOST_NET452_ON_WIN7RTM == m_hrFinal) + { + if (sczUnformattedText) + { + BalFormatString(sczUnformattedText, &sczText); + } + } + else + { + StrAllocFormatted(&sczText, L"0x%08x - %ls", m_hrFinal, sczUnformattedText); + } + + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, sczText); + fShowErrorMessage = TRUE; + } + + if (m_fRestartRequired) + { + if (BOOTSTRAPPER_RESTART_PROMPT == m_command.restart) + { + fShowRestartButton = TRUE; + } + } + + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_LOGFILE_LINK, fShowLogLink); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_MESSAGE_TEXT, fShowErrorMessage); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_RESTART_TEXT, fShowRestartButton); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_RESTART_BUTTON, fShowRestartButton); + + if ((BOOTSTRAPPER_ACTION_INSTALL == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_INSTALL_HEADER)) || + (BOOTSTRAPPER_ACTION_UNINSTALL == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_UNINSTALL_HEADER)) || + (BOOTSTRAPPER_ACTION_REPAIR == m_plannedAction && ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_REPAIR_HEADER))) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_HEADER, FALSE); + } + else + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_HEADER, TRUE); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_INSTALL_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_INSTALL_HEADER, BOOTSTRAPPER_ACTION_INSTALL == m_plannedAction); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_UNINSTALL_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_UNINSTALL_HEADER, BOOTSTRAPPER_ACTION_UNINSTALL == m_plannedAction); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FAILURE_REPAIR_HEADER)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_FAILURE_REPAIR_HEADER, BOOTSTRAPPER_ACTION_REPAIR == m_plannedAction); + } + } + + // Hide the upgrade link + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK)) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_UPGRADE_LINK, FALSE); + } + + // Process each control for special handling in the new page. + THEME_PAGE* pPage = ThemeGetPage(m_pTheme, dwNewPageId); + if (pPage) + { + for (DWORD i = 0; i < pPage->cControlIndices; ++i) + { + THEME_CONTROL* pControl = m_pTheme->rgControls + pPage->rgdwControlIndices[i]; + + // If we are on the install, options or modify pages and this is a named control, try to set its default state. + if ((m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId || + m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS] == dwNewPageId || + m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) && + pControl->sczName && *pControl->sczName) + { + // If this is a checkbox control, try to set its default state to the state of a matching named Burn variable. + if (THEME_CONTROL_TYPE_CHECKBOX == pControl->type && WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX != pControl->wId) + { + LONGLONG llValue = 0; + HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue); + + ThemeSendControlMessage(m_pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); + } + + // If this is a button control with the BS_AUTORADIOBUTTON style, try to set its default + // state to the state of a matching named Burn variable. + if (THEME_CONTROL_TYPE_BUTTON == pControl->type && (BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & pControl->dwStyle))) + { + LONGLONG llValue = 0; + HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue); + + // If the control value isn't set then disable it. + if (!SUCCEEDED(hr)) + { + ThemeControlEnable(m_pTheme, pControl->wId, FALSE); + } + else + { + ThemeSendControlMessage(m_pTheme, pControl->wId, BM_SETCHECK, SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED, 0); + } + } + + // If this is an editbox control, try to set its default state to the state of a matching named Burn variable. + if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && WIXSTDBA_CONTROL_FOLDER_EDITBOX != pControl->wId) + { + LPWSTR sczEditboxValue = NULL; + HRESULT hr = BalGetStringVariable(pControl->sczName, &sczEditboxValue); + + if (SUCCEEDED(hr)) + { + ThemeSetTextControl(m_pTheme, pControl->wId, sczEditboxValue); + } + + ReleaseStr(sczEditboxValue); + } + + // Hide or disable controls based on the control name with 'State' appended + HRESULT hr = StrAllocFormatted(&sczControlName, L"%lsState", pControl->sczName); + if (SUCCEEDED(hr)) + { + hr = BalGetStringVariable(sczControlName, &sczControlState); + if (SUCCEEDED(hr) && sczControlState && *sczControlState) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, sczControlState, -1, L"disable", -1)) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Disable control %ls", pControl->sczName); + ThemeControlEnable(m_pTheme, pControl->wId, FALSE); + } + else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, sczControlState, -1, L"hide", -1)) + { + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hide control %ls", pControl->sczName); + // TODO: This doesn't work + ThemeShowControl(m_pTheme, pControl->wId, SW_HIDE); + } + } + } + } + + // Format the text in each of the new page's controls (if they have any text). + if (pControl->sczText && *pControl->sczText) + { + // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe + // so don't go down the rabbit hole of making sure that this is securely freed. + HRESULT hr = BalFormatString(pControl->sczText, &sczText); + if (SUCCEEDED(hr)) + { + ThemeSetTextControl(m_pTheme, pControl->wId, sczText); + } + } + } + } + + // Exit "modal" pages if any is active. + if (m_rgdwPageIds[WIXSTDBA_PAGE_FILESINUSE] == dwOldPageId) + { + ExitModalState(IDERROR); + } + + ThemeShowPage(m_pTheme, dwOldPageId, SW_HIDE); + ThemeShowPage(m_pTheme, dwNewPageId, SW_SHOW); + + // Remember current page + m_dwCurrentPage = dwNewPageId; + + // On the install page set the focus to the install button or the next enabled control if install is disabled + if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) + { + HWND hwndFocus = ::GetDlgItem(m_pTheme->hwndParent, WIXSTDBA_CONTROL_INSTALL_BUTTON); + if (hwndFocus && !ThemeControlEnabled(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON)) + { + hwndFocus = ::GetNextDlgTabItem(m_pTheme->hwndParent, hwndFocus, FALSE); + } + + if (hwndFocus) + { + ::SetFocus(hwndFocus); + } + } + } + } + + ReleaseStr(sczText); + ReleaseStr(sczUnformattedText); + ReleaseStr(sczControlState); + ReleaseStr(sczControlName); + } + + + // + // OnClose - called when the window is trying to be closed. + // + BOOL OnClose() + { + BOOL fClose = FALSE; + + // If we've already succeeded or failed or showing the help page, just close (prompts are annoying if the bootstrapper is done). + if (WIXSTDBA_STATE_APPLIED <= m_state || WIXSTDBA_STATE_HELP == m_state) + { + fClose = TRUE; + } + else // prompt the user or force the cancel if there is no UI. + { + fClose = PromptCancel(m_hWnd, BOOTSTRAPPER_DISPLAY_FULL != m_command.display, m_sczConfirmCloseMessage ? m_sczConfirmCloseMessage : L"Are you sure you want to cancel?", m_pTheme->sczCaption); + } + + // If we're doing progress then we never close, we just cancel to let rollback occur. + if ((WIXSTDBA_STATE_APPLYING <= m_state && WIXSTDBA_STATE_APPLIED > m_state) || WIXSTDBA_STATE_FILESINUSE == m_state ) + { + // If we canceled disable cancel button since clicking it again is silly. + if (fClose) + { + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_PROGRESS_CANCEL_BUTTON, FALSE); + } + + fClose = FALSE; + } + + return fClose; + } + + + // + // OnClickAcceptCheckbox - allow the install to continue. + // + void OnClickAcceptCheckbox() + { + BOOL fAcceptedLicense = ThemeIsControlChecked(m_pTheme, WIXSTDBA_CONTROL_EULA_ACCEPT_CHECKBOX); + ThemeControlEnable(m_pTheme, WIXSTDBA_CONTROL_INSTALL_BUTTON, fAcceptedLicense); + } + + + // + // OnClickOptionsButton - show the options page. + // + void OnClickOptionsButton() + { + SavePageSettings(WIXSTDBA_PAGE_INSTALL); + m_stateBeforeOptions = m_state; + SetState(WIXSTDBA_STATE_OPTIONS, S_OK); + } + + + // + // OnClickOptionsBrowseButton - browse for install folder on the options page. + // + void OnClickOptionsBrowseButton(DWORD dwControl) + { + WCHAR wzPath[MAX_PATH] = { }; + BROWSEINFOW browseInfo = { }; + PIDLIST_ABSOLUTE pidl = NULL; + PIDLIST_ABSOLUTE pidlRoot = NULL; + + ::SHGetFolderLocation(m_hWnd, CSIDL_DRIVES, NULL, 0, &pidlRoot); + + browseInfo.hwndOwner = m_hWnd; + browseInfo.pszDisplayName = wzPath; + browseInfo.lpszTitle = m_pTheme->sczCaption; + browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; + browseInfo.pidlRoot = pidlRoot; + pidl = ::SHBrowseForFolderW(&browseInfo); + if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) + { + switch (dwControl) + { + case WIXSTDBA_CONTROL_BROWSE_BUTTON: + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, wzPath); + break; + + case WIXSTDBA_CONTROL_BROWSE_BUTTON2: + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, wzPath); + break; + } + } + + if (pidl) + { + ::CoTaskMemFree(pidl); + } + + return; + } + + // + // OnClickOptionsOkButton - accept the changes made by the options page. + // + void OnClickOptionsOkButton() + { + HRESULT hr = S_OK; + LPWSTR sczPath = NULL; + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX)) + { + hr = ThemeGetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX, &sczPath); + ExitOnFailure(hr, "Failed to get text from folder edit box."); + + // TODO: verify the path is valid. + + hr = m_pEngine->SetVariableString(WIXSTDBA_VARIABLE_INSTALL_FOLDER, sczPath); + ExitOnFailure(hr, "Failed to set the install folder."); + } + + if (ThemeControlExists(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2)) + { + hr = ThemeGetTextControl(m_pTheme, WIXSTDBA_CONTROL_FOLDER_EDITBOX2, &sczPath); + ExitOnFailure(hr, "Failed to get text from folder edit box2."); + + // TODO: verify the path is valid. + + hr = m_pEngine->SetVariableString(WIXSTDBA_VARIABLE_INSTALL_FOLDER2, sczPath); + ExitOnFailure(hr, "Failed to set the install folder2."); + } + + SavePageSettings(WIXSTDBA_PAGE_OPTIONS); + + LExit: + SetState(m_stateBeforeOptions, S_OK); + return; + } + + + // + // OnClickOptionsCancelButton - discard the changes made by the options page. + // + void OnClickOptionsCancelButton() + { + SetState(m_stateBeforeOptions, S_OK); + } + + + // + // OnClickInstallButton - start the install by planning the packages. + // + void OnClickInstallButton() + { + SavePageSettings(WIXSTDBA_PAGE_INSTALL); + + this->OnPlan(BOOTSTRAPPER_ACTION_INSTALL); + } + + + // + // OnClickRepairButton - start the repair. + // + void OnClickRepairButton() + { + SavePageSettings(WIXSTDBA_PAGE_MODIFY); + + this->OnPlan(BOOTSTRAPPER_ACTION_REPAIR); + } + + + // + // OnClickUninstallButton - start the uninstall. + // + void OnClickUninstallButton() + { + SavePageSettings(WIXSTDBA_PAGE_MODIFY); + + this->OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL); + } + + + // + // OnClickCloseButton - close the application. + // + void OnClickCloseButton() + { + ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); + } + + + // + // OnClickEulaLink - show the end user license agreement. + // + void OnClickEulaLink() + { + HRESULT hr = S_OK; + LPWSTR sczLicenseUrl = NULL; + LPWSTR sczLicensePath = NULL; + LPWSTR sczLicenseDirectory = NULL; + LPWSTR sczLicenseFilename = NULL; + URI_PROTOCOL protocol = URI_PROTOCOL_UNKNOWN; + + hr = StrAllocString(&sczLicenseUrl, m_sczLicenseUrl, 0); + BalExitOnFailure1(hr, "Failed to copy license URL: %ls", m_sczLicenseUrl); + + hr = LocLocalizeString(m_pWixLoc, &sczLicenseUrl); + BalExitOnFailure1(hr, "Failed to localize license URL: %ls", m_sczLicenseUrl); + + // Assume there is no hidden variables to be formatted + // so don't worry about securely freeing it. + hr = BalFormatString(sczLicenseUrl, &sczLicenseUrl); + BalExitOnFailure1(hr, "Failed to get formatted license URL: %ls", m_sczLicenseUrl); + + hr = UriProtocol(sczLicenseUrl, &protocol); + if (FAILED(hr) || URI_PROTOCOL_UNKNOWN == protocol) + { + // Probe for localized license file + hr = PathRelativeToModule(&sczLicensePath, sczLicenseUrl, m_hModule); + if (SUCCEEDED(hr)) + { + hr = PathGetDirectory(sczLicensePath, &sczLicenseDirectory); + if (SUCCEEDED(hr)) + { + hr = LocProbeForFile(sczLicenseDirectory, PathFile(sczLicenseUrl), m_sczLanguage, &sczLicensePath); + } + } + } + + hr = ShelExecUnelevated(sczLicensePath ? sczLicensePath : sczLicenseUrl, NULL, L"open", NULL, SW_SHOWDEFAULT); + BalExitOnFailure(hr, "Failed to launch URL to EULA."); + + LExit: + ReleaseStr(sczLicensePath); + ReleaseStr(sczLicenseUrl); + ReleaseStr(sczLicenseDirectory); + ReleaseStr(sczLicenseFilename); + + return; + } + + + // + // OnClickUpgradeLink - download the upgrade. + // + void OnClickUpgradeLink() + { + this->OnPlan(BOOTSTRAPPER_ACTION_UPDATE_REPLACE); + + m_fUpdating = TRUE; + + return; + } + + + // + // OnClickLaunchButton - launch the app from the success page. + // + void OnClickLaunchButton() + { + HRESULT hr = S_OK; + LPWSTR sczUnformattedLaunchTarget = NULL; + LPWSTR sczLaunchTarget = NULL; + LPWSTR sczLaunchTargetElevatedId = NULL; + LPWSTR sczUnformattedArguments = NULL; + LPWSTR sczArguments = NULL; + LPWSTR sczLaunchFolder = NULL; + int nCmdShow = SW_SHOWNORMAL; + + hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH, &sczUnformattedLaunchTarget); + BalExitOnFailure1(hr, "Failed to get launch target variable '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_TARGET_PATH); + + hr = BalFormatString(sczUnformattedLaunchTarget, &sczLaunchTarget); + BalExitOnFailure1(hr, "Failed to format launch target variable: %ls", sczUnformattedLaunchTarget); + + if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID)) + { + hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID, &sczLaunchTargetElevatedId); + BalExitOnFailure1(hr, "Failed to get launch target elevated id '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_TARGET_ELEVATED_ID); + } + + if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS)) + { + hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS, &sczUnformattedArguments); + BalExitOnFailure1(hr, "Failed to get launch arguments '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_ARGUMENTS); + } + + if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_HIDDEN)) + { + nCmdShow = SW_HIDE; + } + + if (BalStringVariableExists(WIXSTDBA_VARIABLE_LAUNCH_WORK_FOLDER)) + { + hr = BalGetStringVariable(WIXSTDBA_VARIABLE_LAUNCH_WORK_FOLDER, &sczLaunchFolder); + BalExitOnFailure1(hr, "Failed to get launch working directory variable '%ls'.", WIXSTDBA_VARIABLE_LAUNCH_WORK_FOLDER); + } + + if (sczLaunchTargetElevatedId && !m_fTriedToLaunchElevated) + { + m_fTriedToLaunchElevated = TRUE; + hr = m_pEngine->LaunchApprovedExe(m_hWnd, sczLaunchTargetElevatedId, sczUnformattedArguments, 0); + if (FAILED(hr)) + { + BalLogError(hr, "Failed to launch elevated target: %ls", sczLaunchTargetElevatedId); + + //try with ShelExec next time + OnClickLaunchButton(); + } + } + else + { + if (sczUnformattedArguments) + { + hr = BalFormatString(sczUnformattedArguments, &sczArguments); + BalExitOnFailure1(hr, "Failed to format launch arguments variable: %ls", sczUnformattedArguments); + } + + hr = ShelExec(sczLaunchTarget, sczArguments, L"open", sczLaunchFolder, nCmdShow, m_hWnd, NULL); + BalExitOnFailure1(hr, "Failed to launch target: %ls", sczLaunchTarget); + + ::PostMessageW(m_hWnd, WM_CLOSE, 0, 0); + } + + LExit: + ReleaseStr(sczLaunchFolder); + StrSecureZeroFreeString(sczArguments); + ReleaseStr(sczUnformattedArguments); + ReleaseStr(sczLaunchTargetElevatedId); + StrSecureZeroFreeString(sczLaunchTarget); + ReleaseStr(sczUnformattedLaunchTarget); + + return; + } + + + // + // OnClickRestartButton - allows the restart and closes the app. + // + void OnClickRestartButton() + { + AssertSz(m_fRestartRequired, "Restart must be requested to be able to click on the restart button."); + + m_fAllowRestart = TRUE; + ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); + + return; + } + + + // + // OnClickLogFileLink - show the log file. + // + void OnClickLogFileLink() + { + HRESULT hr = S_OK; + LPWSTR sczLogFile = NULL; + + hr = BalGetStringVariable(m_Bundle.sczLogVariable, &sczLogFile); + BalExitOnFailure1(hr, "Failed to get log file variable '%ls'.", m_Bundle.sczLogVariable); + + hr = ShelExecUnelevated(L"notepad.exe", sczLogFile, L"open", NULL, SW_SHOWDEFAULT); + BalExitOnFailure1(hr, "Failed to open log file target: %ls", sczLogFile); + + LExit: + ReleaseStr(sczLogFile); + + return; + } + + + // + // OnClickFilesInUseOkButton + // + void OnClickFilesInUseOkButton() + { + BOOL fCloseApps = ThemeIsControlChecked(m_pTheme, WIXSTDBA_CONTROL_FILESINUSE_CLOSE_RADIOBUTTON); + ExitModalState(fCloseApps ? IDOK : IDIGNORE); + } + + // + // SetState + // + void SetState( + __in WIXSTDBA_STATE state, + __in HRESULT hrStatus + ) + { + if (FAILED(hrStatus)) + { + m_hrFinal = hrStatus; + } + + if (FAILED(m_hrFinal)) + { + state = WIXSTDBA_STATE_FAILED; + } + + if (WIXSTDBA_STATE_OPTIONS == state || WIXSTDBA_STATE_FILESINUSE == state || m_state < state) + { + ::PostMessageW(m_hWnd, WM_WIXSTDBA_CHANGE_STATE, 0, state); + } + } + + + void DeterminePageId( + __in WIXSTDBA_STATE state, + __out DWORD* pdwPageId + ) + { + if (BOOTSTRAPPER_DISPLAY_PASSIVE == m_command.display) + { + switch (state) + { + case WIXSTDBA_STATE_INITIALIZED: + *pdwPageId = BOOTSTRAPPER_ACTION_HELP == m_command.action ? m_rgdwPageIds[WIXSTDBA_PAGE_HELP] : m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; + break; + + case WIXSTDBA_STATE_HELP: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_HELP]; + break; + + case WIXSTDBA_STATE_DETECTING: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_LOADING] ? m_rgdwPageIds[WIXSTDBA_PAGE_LOADING] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] ? m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; + break; + + case WIXSTDBA_STATE_DETECTED: __fallthrough; + case WIXSTDBA_STATE_PLANNING: __fallthrough; + case WIXSTDBA_STATE_PLANNED: __fallthrough; + case WIXSTDBA_STATE_APPLYING: __fallthrough; + case WIXSTDBA_STATE_CACHING: __fallthrough; + case WIXSTDBA_STATE_CACHED: __fallthrough; + case WIXSTDBA_STATE_EXECUTING: __fallthrough; + case WIXSTDBA_STATE_EXECUTED: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] ? m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS_PASSIVE] : m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; + break; + + default: + *pdwPageId = 0; + break; + } + } + else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) + { + switch (state) + { + case WIXSTDBA_STATE_INITIALIZING: + *pdwPageId = 0; + break; + + case WIXSTDBA_STATE_INITIALIZED: + *pdwPageId = BOOTSTRAPPER_ACTION_HELP == m_command.action ? m_rgdwPageIds[WIXSTDBA_PAGE_HELP] : m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; + break; + + case WIXSTDBA_STATE_HELP: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_HELP]; + break; + + case WIXSTDBA_STATE_DETECTING: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_LOADING]; + break; + + case WIXSTDBA_STATE_DETECTED: + switch (m_command.action) + { + case BOOTSTRAPPER_ACTION_INSTALL: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL]; + break; + + case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough; + case BOOTSTRAPPER_ACTION_REPAIR: __fallthrough; + case BOOTSTRAPPER_ACTION_UNINSTALL: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY]; + break; + } + break; + + case WIXSTDBA_STATE_OPTIONS: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_OPTIONS]; + break; + + case WIXSTDBA_STATE_FILESINUSE: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_FILESINUSE]; + break; + + case WIXSTDBA_STATE_PLANNING: __fallthrough; + case WIXSTDBA_STATE_PLANNED: __fallthrough; + case WIXSTDBA_STATE_APPLYING: __fallthrough; + case WIXSTDBA_STATE_CACHING: __fallthrough; + case WIXSTDBA_STATE_CACHED: __fallthrough; + case WIXSTDBA_STATE_EXECUTING: __fallthrough; + case WIXSTDBA_STATE_EXECUTED: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_PROGRESS]; + break; + + case WIXSTDBA_STATE_APPLIED: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS]; + break; + + case WIXSTDBA_STATE_FAILED: + *pdwPageId = m_rgdwPageIds[WIXSTDBA_PAGE_FAILURE]; + break; + } + } + } + + + HRESULT EvaluateConditions() + { + HRESULT hr = S_OK; + BOOL fResult = FALSE; + + for (DWORD i = 0; i < m_Conditions.cConditions; ++i) + { + BAL_CONDITION* pCondition = m_Conditions.rgConditions + i; + + hr = BalConditionEvaluate(pCondition, m_pEngine, &fResult, &m_sczFailedMessage); + BalExitOnFailure(hr, "Failed to evaluate condition."); + + if (!fResult) + { + // Hope they didn't have hidden variables in their message, because it's going in the log in plaintext. + BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "%ls", m_sczFailedMessage); + + hr = E_WIXSTDBA_CONDITION_FAILED; + // todo: remove in WiX v4, in case people are relying on v3.x logging behavior + BalExitOnFailure1(hr, "Bundle condition evaluated to false: %ls", pCondition->sczCondition); + } + } + + ReleaseNullStrSecure(m_sczFailedMessage); + + LExit: + return hr; + } + + + void SetTaskbarButtonProgress( + __in DWORD dwOverallPercentage + ) + { + HRESULT hr = S_OK; + + if (m_fTaskbarButtonOK) + { + hr = m_pTaskbarList->SetProgressValue(m_hWnd, dwOverallPercentage, 100UL); + BalExitOnFailure1(hr, "Failed to set taskbar button progress to: %d%%.", dwOverallPercentage); + } + + LExit: + return; + } + + + void SetTaskbarButtonState( + __in TBPFLAG tbpFlags + ) + { + HRESULT hr = S_OK; + + if (m_fTaskbarButtonOK) + { + hr = m_pTaskbarList->SetProgressState(m_hWnd, tbpFlags); + BalExitOnFailure1(hr, "Failed to set taskbar button state.", tbpFlags); + } + + LExit: + return; + } + + + void SetProgressState( + __in HRESULT hrStatus + ) + { + TBPFLAG flag = TBPF_NORMAL; + + if (IsCanceled() || HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hrStatus) + { + flag = TBPF_PAUSED; + } + else if (IsRollingBack() || FAILED(hrStatus)) + { + flag = TBPF_ERROR; + } + + SetTaskbarButtonState(flag); + } + + + HRESULT LoadBootstrapperBAFunctions() + { + HRESULT hr = S_OK; + LPWSTR sczBafPath = NULL; + + hr = PathRelativeToModule(&sczBafPath, L"bafunctions.dll", m_hModule); + BalExitOnFailure(hr, "Failed to get path to BA function DLL."); #ifdef DEBUG - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXEXTBA: LoadBootstrapperBAFunctions() - BA function DLL '%ls'", sczBafPath); + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXEXTBA: LoadBootstrapperBAFunctions() - BA function DLL '%ls'", sczBafPath); #endif + + m_hBAFModule = ::LoadLibraryW(sczBafPath); + if (m_hBAFModule) + { + PFN_BOOTSTRAPPER_BA_FUNCTION_CREATE pfnBAFunctionCreate = reinterpret_cast(::GetProcAddress(m_hBAFModule, "CreateBootstrapperBAFunction")); + BalExitOnNullWithLastError1(pfnBAFunctionCreate, hr, "Failed to get CreateBootstrapperBAFunction entry-point from: %ls", sczBafPath); - m_hBAFModule = ::LoadLibraryW(sczBafPath); - if (m_hBAFModule) - { - PFN_BOOTSTRAPPER_BA_FUNCTION_CREATE pfnBAFunctionCreate = reinterpret_cast(::GetProcAddress(m_hBAFModule, "CreateBootstrapperBAFunction")); - BalExitOnNullWithLastError1(pfnBAFunctionCreate, hr, "Failed to get CreateBootstrapperBAFunction entry-point from: %ls", sczBafPath); - - hr = pfnBAFunctionCreate(m_pEngine, m_hBAFModule, &m_pBAFunction); - BalExitOnFailure(hr, "Failed to create BA function."); - } + hr = pfnBAFunctionCreate(m_pEngine, m_hBAFModule, &m_pBAFunction); + BalExitOnFailure(hr, "Failed to create BA function."); + } #ifdef DEBUG - else - { - BalLogError(HRESULT_FROM_WIN32(::GetLastError()), "WIXEXTBA: LoadBootstrapperBAFunctions() - Failed to load DLL %ls", sczBafPath); - } + else + { + BalLogError(HRESULT_FROM_WIN32(::GetLastError()), "WIXEXTBA: LoadBootstrapperBAFunctions() - Failed to load DLL %ls", sczBafPath); + } #endif - LExit: - if (m_hBAFModule && !m_pBAFunction) - { - ::FreeLibrary(m_hBAFModule); - m_hBAFModule = NULL; - } - ReleaseStr(sczBafPath); - - return hr; - } + LExit: + if (m_hBAFModule && !m_pBAFunction) + { + ::FreeLibrary(m_hBAFModule); + m_hBAFModule = NULL; + } + ReleaseStr(sczBafPath); + + return hr; + } - void SavePageSettings( - __in WIXSTDBA_PAGE page - ) - { - THEME_PAGE* pPage = NULL; + void SavePageSettings( + __in WIXSTDBA_PAGE page + ) + { + THEME_PAGE* pPage = NULL; - pPage = ThemeGetPage(m_pTheme, m_rgdwPageIds[page]); - if (pPage) - { - for (DWORD i = 0; i < pPage->cControlIndices; ++i) - { - // Loop through all the checkbox controls (or buttons with BS_AUTORADIOBUTTON) with names and set a Burn variable with that name to true or false. - THEME_CONTROL* pControl = m_pTheme->rgControls + pPage->rgdwControlIndices[i]; - if ((THEME_CONTROL_TYPE_CHECKBOX == pControl->type) || - (THEME_CONTROL_TYPE_BUTTON == pControl->type && (BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & pControl->dwStyle)) && - pControl->sczName && *pControl->sczName)) - { - BOOL bChecked = ThemeIsControlChecked(m_pTheme, pControl->wId); - m_pEngine->SetVariableNumeric(pControl->sczName, bChecked ? 1 : 0); - } + pPage = ThemeGetPage(m_pTheme, m_rgdwPageIds[page]); + if (pPage) + { + for (DWORD i = 0; i < pPage->cControlIndices; ++i) + { + // Loop through all the checkbox controls (or buttons with BS_AUTORADIOBUTTON) with names and set a Burn variable with that name to true or false. + THEME_CONTROL* pControl = m_pTheme->rgControls + pPage->rgdwControlIndices[i]; + if ((THEME_CONTROL_TYPE_CHECKBOX == pControl->type) || + (THEME_CONTROL_TYPE_BUTTON == pControl->type && (BS_AUTORADIOBUTTON == (BS_AUTORADIOBUTTON & pControl->dwStyle)) && + pControl->sczName && *pControl->sczName)) + { + BOOL bChecked = ThemeIsControlChecked(m_pTheme, pControl->wId); + m_pEngine->SetVariableNumeric(pControl->sczName, bChecked ? 1 : 0); + } - // Loop through all the editbox controls with names and set a Burn variable with that name to the contents. - if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName && - (WIXSTDBA_CONTROL_FOLDER_EDITBOX != pControl->wId && WIXSTDBA_CONTROL_FOLDER_EDITBOX2 != pControl->wId)) - { - LPWSTR sczValue = NULL; - ThemeGetTextControl(m_pTheme, pControl->wId, &sczValue); - m_pEngine->SetVariableString(pControl->sczName, sczValue); - } - } - } - } + // Loop through all the editbox controls with names and set a Burn variable with that name to the contents. + if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName && + (WIXSTDBA_CONTROL_FOLDER_EDITBOX != pControl->wId && WIXSTDBA_CONTROL_FOLDER_EDITBOX2 != pControl->wId)) + { + LPWSTR sczValue = NULL; + ThemeGetTextControl(m_pTheme, pControl->wId, &sczValue); + m_pEngine->SetVariableString(pControl->sczName, sczValue); + } + } + } + } + int ShowFilesInUseModal( + __in DWORD cFiles, + __in_ecount_z(cFiles) LPCWSTR* rgwzFiles + ) + { + HRESULT hr = S_OK; + LPWSTR sczFilesInUse = NULL; + DWORD_PTR cchLen = 0; + int nResult = IDERROR; + + // If the user has choosen to ignore on a previously displayed "files in use" page, + // we will return the same result for other cases. No need to display the page again. + if (IDIGNORE == m_nLastFilesInUseResult) + { + nResult = m_nLastFilesInUseResult; + } + else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) // Only show files in use when using full display mode. + { + // Show applications using the files. + if (cFiles > 0) + { + // See https://msdn.microsoft.com/en-us/library/aa371614%28v=vs.85%29.aspx for details. + for (DWORD i = 1; i < cFiles; i += 2) + { + hr = ::StringCchLengthW(rgwzFiles[i], STRSAFE_MAX_CCH, reinterpret_cast(&cchLen)); + BalExitOnFailure(hr, "Failed to calculate length of string"); + + if (cchLen > 0) + { + hr = StrAllocConcat(&sczFilesInUse, rgwzFiles[i], 0); + BalExitOnFailure(hr, "Failed to concat files in use"); + + hr = StrAllocConcat(&sczFilesInUse, L"\r\n", 2); + BalExitOnFailure(hr, "Failed to concat files in use"); + } + } + } + + if (sczFilesInUse) + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FILESINUSE_TEXT, sczFilesInUse); + } + else + { + ThemeSetTextControl(m_pTheme, WIXSTDBA_CONTROL_FILESINUSE_TEXT, L""); + } + + // Select default option (restart applications). + if (IDNOACTION == m_nLastFilesInUseResult) + { + ThemeSendControlMessage(m_pTheme, WIXSTDBA_CONTROL_FILESINUSE_CLOSE_RADIOBUTTON, BM_SETCHECK, BST_CHECKED, 0); + } + + nResult = ShowModalState(WIXSTDBA_STATE_FILESINUSE); + } + else + { + // Silent UI level installations always shut down applications and services, + // and on Windows Vista and later, use Restart Manager unless disabled. + nResult = IDOK; + } + + LExit: + ReleaseStr(sczFilesInUse); + + // Remember the answer from the user. + m_nLastFilesInUseResult = FAILED(hr) ? IDERROR : nResult; + + return m_nLastFilesInUseResult; + } + + int ShowModalState( + __in WIXSTDBA_STATE state + ) + { + int nResult = IDERROR; + + //Show state and wait for result + nResult = ::SendMessageW(m_hWnd, WM_WIXSTDBA_SHOW_STATE_MODAL, 0, state); + + return nResult; + } + + void ExitModalState( + __in int result + ) + { + if (m_cModalPages > 0) + { + ::PostMessage(m_hWnd, WM_WIXSTDBA_CLOSE_STATE_MODAL, 0, result); + } + } public: - // - // Constructor - intitialize member variables. - // - CWixStandardBootstrapperApplication( - __in HMODULE hModule, - __in BOOL fPrereq, - __in IBootstrapperEngine* pEngine, - __in const BOOTSTRAPPER_COMMAND* pCommand - ) : CBalBaseBootstrapperApplication(pEngine, pCommand, 3, 3000) - { - m_hModule = hModule; - memcpy_s(&m_command, sizeof(m_command), pCommand, sizeof(BOOTSTRAPPER_COMMAND)); + // + // Constructor - initialize member variables. + // + CWixStandardBootstrapperApplication( + __in HMODULE hModule, + __in BOOL fPrereq, + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in const BOOTSTRAPPER_COMMAND* pCommand + ) : CBalBaseBootstrapperApplication(pEngine, pCommand, 3, 3000) + { + m_hModule = hModule; + memcpy_s(&m_command, sizeof(m_command), pCommand, sizeof(BOOTSTRAPPER_COMMAND)); - // Pre-req BA should only show help or do an install (to launch the Managed BA which can then do the right action). - if (fPrereq && BOOTSTRAPPER_ACTION_HELP != m_command.action && BOOTSTRAPPER_ACTION_INSTALL != m_command.action) - { - m_command.action = BOOTSTRAPPER_ACTION_INSTALL; - } - else // maybe modify the action state if the bundle is or is not already installed. - { - LONGLONG llInstalled = 0; - HRESULT hr = BalGetNumericVariable(L"WixBundleInstalled", &llInstalled); - if (SUCCEEDED(hr) && BOOTSTRAPPER_RESUME_TYPE_REBOOT != m_command.resumeType && 0 < llInstalled && BOOTSTRAPPER_ACTION_INSTALL == m_command.action) - { - m_command.action = BOOTSTRAPPER_ACTION_MODIFY; - } - else if (0 == llInstalled && (BOOTSTRAPPER_ACTION_MODIFY == m_command.action || BOOTSTRAPPER_ACTION_REPAIR == m_command.action)) - { - m_command.action = BOOTSTRAPPER_ACTION_INSTALL; - } - } + // Pre-req BA should only show help or do an install (to launch the Managed BA which can then do the right action). + if (fPrereq && BOOTSTRAPPER_ACTION_HELP != m_command.action && BOOTSTRAPPER_ACTION_INSTALL != m_command.action) + { + m_command.action = BOOTSTRAPPER_ACTION_INSTALL; + } + else // maybe modify the action state if the bundle is or is not already installed. + { + LONGLONG llInstalled = 0; + HRESULT hr = BalGetNumericVariable(L"WixBundleInstalled", &llInstalled); + if (SUCCEEDED(hr) && BOOTSTRAPPER_RESUME_TYPE_REBOOT != m_command.resumeType && 0 < llInstalled && BOOTSTRAPPER_ACTION_INSTALL == m_command.action) + { + m_command.action = BOOTSTRAPPER_ACTION_MODIFY; + } + else if (0 == llInstalled && (BOOTSTRAPPER_ACTION_MODIFY == m_command.action || BOOTSTRAPPER_ACTION_REPAIR == m_command.action)) + { + m_command.action = BOOTSTRAPPER_ACTION_INSTALL; + } + } - m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN; + m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN; - // When resuming from restart doing some install-like operation, try to find the package that forced the - // restart. We'll use this information during planning. - m_sczAfterForcedRestartPackage = NULL; + // When resuming from restart doing some install-like operation, try to find the package that forced the + // restart. We'll use this information during planning. + m_sczAfterForcedRestartPackage = NULL; - if (BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType && BOOTSTRAPPER_ACTION_UNINSTALL < m_command.action) - { - // Ensure the forced restart package variable is null when it is an empty string. - HRESULT hr = BalGetStringVariable(L"WixBundleForcedRestartPackage", &m_sczAfterForcedRestartPackage); - if (FAILED(hr) || !m_sczAfterForcedRestartPackage || !*m_sczAfterForcedRestartPackage) - { - ReleaseNullStr(m_sczAfterForcedRestartPackage); - } - } + if (BOOTSTRAPPER_RESUME_TYPE_REBOOT == m_command.resumeType && BOOTSTRAPPER_ACTION_UNINSTALL < m_command.action) + { + // Ensure the forced restart package variable is null when it is an empty string. + HRESULT hr = BalGetStringVariable(L"WixBundleForcedRestartPackage", &m_sczAfterForcedRestartPackage); + if (FAILED(hr) || !m_sczAfterForcedRestartPackage || !*m_sczAfterForcedRestartPackage) + { + ReleaseNullStr(m_sczAfterForcedRestartPackage); + } + } - m_pWixLoc = NULL; - memset(&m_Bundle, 0, sizeof(m_Bundle)); - memset(&m_Conditions, 0, sizeof(m_Conditions)); - m_sczConfirmCloseMessage = NULL; - m_sczFailedMessage = NULL; + m_pWixLoc = NULL; + memset(&m_Bundle, 0, sizeof(m_Bundle)); + memset(&m_Conditions, 0, sizeof(m_Conditions)); + m_sczConfirmCloseMessage = NULL; + m_sczFailedMessage = NULL; - m_sczLanguage = NULL; - m_pTheme = NULL; - memset(m_rgdwPageIds, 0, sizeof(m_rgdwPageIds)); - m_dwCurrentPage = 0; - m_hUiThread = NULL; - m_fRegistered = FALSE; - m_hWnd = NULL; + m_sczLanguage = NULL; + m_pTheme = NULL; + memset(m_rgdwPageIds, 0, sizeof(m_rgdwPageIds)); + m_dwCurrentPage = 0; + m_hUiThread = NULL; + m_fRegistered = FALSE; + m_hWnd = NULL; - m_state = WIXSTDBA_STATE_INITIALIZING; - m_hrFinal = S_OK; + m_state = WIXSTDBA_STATE_INITIALIZING; + m_hrFinal = hrHostInitialization; - m_fDowngrading = FALSE; - m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE; - m_fRestartRequired = FALSE; - m_fAllowRestart = FALSE; + m_fDowngrading = FALSE; + m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE; + m_fRestartRequired = FALSE; + m_fAllowRestart = FALSE; - m_sczLicenseFile = NULL; - m_sczLicenseUrl = NULL; - m_fSuppressOptionsUI = FALSE; - m_fSuppressDowngradeFailure = FALSE; - m_fSuppressRepair = FALSE; - m_fShowVersion = FALSE; + m_sczLicenseFile = NULL; + m_sczLicenseUrl = NULL; + m_fSuppressOptionsUI = FALSE; + m_fSuppressDowngradeFailure = FALSE; + m_fSuppressRepair = FALSE; + m_fShowVersion = FALSE; + m_fSupportCacheOnly = FALSE; + m_fShowFilesInUse = FALSE; - m_sdOverridableVariables = NULL; - m_pTaskbarList = NULL; - m_uTaskbarButtonCreatedMessage = UINT_MAX; - m_fTaskbarButtonOK = FALSE; - m_fShowingInternalUiThisPackage = FALSE; + m_sdOverridableVariables = NULL; + m_shPrereqSupportPackages = NULL; + m_rgPrereqPackages = NULL; + m_cPrereqPackages = 0; + m_pTaskbarList = NULL; + m_uTaskbarButtonCreatedMessage = UINT_MAX; + m_fTaskbarButtonOK = FALSE; + m_fShowingInternalUiThisPackage = FALSE; + m_fTriedToLaunchElevated = FALSE; - m_fPrereq = fPrereq; - m_sczPrereqPackage = NULL; - m_fPrereqInstalled = FALSE; - m_fPrereqAlreadyInstalled = FALSE; + m_fPrereq = fPrereq; + m_sczPrereqPackage = NULL; + m_fPrereqInstalled = FALSE; + m_fPrereqAlreadyInstalled = FALSE; - m_fUpdating = FALSE; + m_fUpdating = FALSE; - pEngine->AddRef(); - m_pEngine = pEngine; + m_cModalPages = 0; + m_nLastFilesInUseResult = IDNOACTION; - m_hBAFModule = NULL; - m_pBAFunction = NULL; - } + pEngine->AddRef(); + m_pEngine = pEngine; + + m_hBAFModule = NULL; + m_pBAFunction = NULL; + } - // - // Destructor - release member variables. - // - ~CWixStandardBootstrapperApplication() - { - AssertSz(!::IsWindow(m_hWnd), "Window should have been destroyed before destructor."); - AssertSz(!m_pTheme, "Theme should have been released before destuctor."); + // + // Destructor - release member variables. + // + ~CWixStandardBootstrapperApplication() + { + AssertSz(!::IsWindow(m_hWnd), "Window should have been destroyed before destructor."); + AssertSz(!m_pTheme, "Theme should have been released before destructor."); - ReleaseObject(m_pTaskbarList); - ReleaseDict(m_sdOverridableVariables); - ReleaseStr(m_sczFailedMessage); - ReleaseStr(m_sczConfirmCloseMessage); - BalConditionsUninitialize(&m_Conditions); - BalInfoUninitialize(&m_Bundle); - LocFree(m_pWixLoc); + ReleaseObject(m_pTaskbarList); + ReleaseDict(m_sdOverridableVariables); + ReleaseDict(m_shPrereqSupportPackages); + ReleaseMem(m_rgPrereqPackages); + ReleaseStr(m_sczFailedMessage); + ReleaseStr(m_sczConfirmCloseMessage); + BalConditionsUninitialize(&m_Conditions); + BalInfoUninitialize(&m_Bundle); + LocFree(m_pWixLoc); - ReleaseStr(m_sczLanguage); - ReleaseStr(m_sczLicenseFile); - ReleaseStr(m_sczLicenseUrl); - ReleaseStr(m_sczPrereqPackage); - ReleaseStr(m_sczAfterForcedRestartPackage); - ReleaseNullObject(m_pEngine); + ReleaseStr(m_sczLanguage); + ReleaseStr(m_sczLicenseFile); + ReleaseStr(m_sczLicenseUrl); + ReleaseStr(m_sczPrereqPackage); + ReleaseStr(m_sczAfterForcedRestartPackage); + ReleaseNullObject(m_pEngine); - if (m_hBAFModule) - { - ::FreeLibrary(m_hBAFModule); - m_hBAFModule = NULL; - } - } + if (m_hBAFModule) + { + ::FreeLibrary(m_hBAFModule); + m_hBAFModule = NULL; + } + } private: - HMODULE m_hModule; - BOOTSTRAPPER_COMMAND m_command; - IBootstrapperEngine* m_pEngine; - BOOTSTRAPPER_ACTION m_plannedAction; + HMODULE m_hModule; + BOOTSTRAPPER_COMMAND m_command; + IBootstrapperEngine* m_pEngine; + BOOTSTRAPPER_ACTION m_plannedAction; - LPWSTR m_sczAfterForcedRestartPackage; + LPWSTR m_sczAfterForcedRestartPackage; - WIX_LOCALIZATION* m_pWixLoc; - BAL_INFO_BUNDLE m_Bundle; - BAL_CONDITIONS m_Conditions; - LPWSTR m_sczFailedMessage; - LPWSTR m_sczConfirmCloseMessage; + WIX_LOCALIZATION* m_pWixLoc; + BAL_INFO_BUNDLE m_Bundle; + BAL_CONDITIONS m_Conditions; + LPWSTR m_sczFailedMessage; + LPWSTR m_sczConfirmCloseMessage; - LPWSTR m_sczLanguage; - THEME* m_pTheme; - DWORD m_rgdwPageIds[countof(vrgwzPageNames)]; - DWORD m_dwCurrentPage; - HANDLE m_hUiThread; - BOOL m_fRegistered; - HWND m_hWnd; + LPWSTR m_sczLanguage; + THEME* m_pTheme; + DWORD m_rgdwPageIds[countof(vrgwzPageNames)]; + DWORD m_dwCurrentPage; + HANDLE m_hUiThread; + BOOL m_fRegistered; + HWND m_hWnd; - WIXSTDBA_STATE m_state; - WIXSTDBA_STATE m_stateBeforeOptions; - HRESULT m_hrFinal; + WIXSTDBA_STATE m_state; + WIXSTDBA_STATE m_stateBeforeOptions; + HRESULT m_hrFinal; - BOOL m_fStartedExecution; - DWORD m_dwCalculatedCacheProgress; - DWORD m_dwCalculatedExecuteProgress; + BOOL m_fStartedExecution; + DWORD m_dwCalculatedCacheProgress; + DWORD m_dwCalculatedExecuteProgress; - BOOL m_fDowngrading; - BOOTSTRAPPER_APPLY_RESTART m_restartResult; - BOOL m_fRestartRequired; - BOOL m_fAllowRestart; + BOOL m_fDowngrading; + BOOTSTRAPPER_APPLY_RESTART m_restartResult; + BOOL m_fRestartRequired; + BOOL m_fAllowRestart; - LPWSTR m_sczLicenseFile; - LPWSTR m_sczLicenseUrl; - BOOL m_fSuppressOptionsUI; - BOOL m_fSuppressDowngradeFailure; - BOOL m_fSuppressRepair; - BOOL m_fShowVersion; + LPWSTR m_sczLicenseFile; + LPWSTR m_sczLicenseUrl; + BOOL m_fSuppressOptionsUI; + BOOL m_fSuppressDowngradeFailure; + BOOL m_fSuppressRepair; + BOOL m_fShowVersion; + BOOL m_fSupportCacheOnly; + BOOL m_fShowFilesInUse; BOOL m_fLaunchPassive; BOOL m_fLaunchQuiet; - STRINGDICT_HANDLE m_sdOverridableVariables; + STRINGDICT_HANDLE m_sdOverridableVariables; - BOOL m_fPrereq; - LPWSTR m_sczPrereqPackage; - BOOL m_fPrereqInstalled; - BOOL m_fPrereqAlreadyInstalled; + WIXSTDBA_PREREQ_PACKAGE* m_rgPrereqPackages; + DWORD m_cPrereqPackages; + STRINGDICT_HANDLE m_shPrereqSupportPackages; - ITaskbarList3* m_pTaskbarList; - UINT m_uTaskbarButtonCreatedMessage; - BOOL m_fTaskbarButtonOK; - BOOL m_fShowingInternalUiThisPackage; + BOOL m_fPrereq; + LPWSTR m_sczPrereqPackage; + BOOL m_fPrereqInstalled; + BOOL m_fPrereqAlreadyInstalled; - BOOL m_fUpdating; - LPCWSTR m_wzUpdateLocation; + ITaskbarList3* m_pTaskbarList; + UINT m_uTaskbarButtonCreatedMessage; + BOOL m_fTaskbarButtonOK; + BOOL m_fShowingInternalUiThisPackage; + BOOL m_fTriedToLaunchElevated; + BOOL m_fUpdating; + LPCWSTR m_wzUpdateLocation; - HMODULE m_hBAFModule; - IBootstrapperBAFunction* m_pBAFunction; + DWORD m_cModalPages; + int m_nLastFilesInUseResult; + + HMODULE m_hBAFModule; + IBootstrapperBAFunction* m_pBAFunction; }; // -// CreateUserExperience - creates a new IBurnUserExperience object. +// CreateBootstrapperApplication - creates a new IBootstrapperApplication object. // HRESULT CreateBootstrapperApplication( - __in HMODULE hModule, - __in BOOL fPrereq, - __in IBootstrapperEngine* pEngine, - __in const BOOTSTRAPPER_COMMAND* pCommand, - __out IBootstrapperApplication** ppApplication - ) + __in HMODULE hModule, + __in BOOL fPrereq, + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in const BOOTSTRAPPER_COMMAND* pCommand, + __out IBootstrapperApplication** ppApplication + ) { - HRESULT hr = S_OK; - CWixStandardBootstrapperApplication* pApplication = NULL; + HRESULT hr = S_OK; + CWixStandardBootstrapperApplication* pApplication = NULL; - pApplication = new CWixStandardBootstrapperApplication(hModule, fPrereq, pEngine, pCommand); - ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new standard bootstrapper application object."); + pApplication = new CWixStandardBootstrapperApplication(hModule, fPrereq, hrHostInitialization, pEngine, pCommand); + ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new standard bootstrapper application object."); - *ppApplication = pApplication; - pApplication = NULL; + *ppApplication = pApplication; + pApplication = NULL; LExit: - ReleaseObject(pApplication); - return hr; + ReleaseObject(pApplication); + return hr; } diff --git a/wixstdba/precomp.h b/wixstdba/precomp.h index 61bee4f..059d1c1 100644 --- a/wixstdba/precomp.h +++ b/wixstdba/precomp.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "dutil.h" #include "memutil.h" @@ -47,6 +48,7 @@ HRESULT CreateBootstrapperApplication( __in HMODULE hModule, __in BOOL fPrereq, + __in HRESULT hrHostInitialization, __in IBootstrapperEngine* pEngine, __in const BOOTSTRAPPER_COMMAND* pCommand, __out IBootstrapperApplication** ppApplication diff --git a/wixstdba/wixstdba.cpp b/wixstdba/wixstdba.cpp index 59c389c..dcee52a 100644 --- a/wixstdba/wixstdba.cpp +++ b/wixstdba/wixstdba.cpp @@ -47,7 +47,7 @@ extern "C" HRESULT WINAPI BootstrapperApplicationCreate( BalInitialize(pEngine); - hr = CreateBootstrapperApplication(vhInstance, FALSE, pEngine, pCommand, ppApplication); + hr = CreateBootstrapperApplication(vhInstance, FALSE, S_OK, pEngine, pCommand, ppApplication); BalExitOnFailure(hr, "Failed to create bootstrapper application interface."); LExit: @@ -59,3 +59,28 @@ extern "C" void WINAPI BootstrapperApplicationDestroy() { BalUninitialize(); } + + +extern "C" HRESULT WINAPI MbaPrereqBootstrapperApplicationCreate( + __in HRESULT hrHostInitialization, + __in IBootstrapperEngine* pEngine, + __in const BOOTSTRAPPER_COMMAND* pCommand, + __out IBootstrapperApplication** ppApplication + ) +{ + HRESULT hr = S_OK; + + BalInitialize(pEngine); + + hr = CreateBootstrapperApplication(vhInstance, TRUE, hrHostInitialization, pEngine, pCommand, ppApplication); + BalExitOnFailure(hr, "Failed to create managed prerequisite bootstrapper application interface."); + +LExit: + return hr; +} + + +extern "C" void WINAPI MbaPrereqBootstrapperApplicationDestroy() +{ + BalUninitialize(); +} diff --git a/wixstdba/wixstdba.vcxproj b/wixstdba/wixstdba.vcxproj index 3bde57e..44715cb 100644 --- a/wixstdba/wixstdba.vcxproj +++ b/wixstdba/wixstdba.vcxproj @@ -62,7 +62,7 @@ Disabled - $(WiX)SDK\VS2010\inc;%(AdditionalIncludeDirectories) + $(WiX)SDK\VS2013\inc;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;DEBUG;%(PreprocessorDefinitions);$(ExtraDefinitions) true EnableFastChecks @@ -80,7 +80,7 @@ dutil.lib;balutil.lib;comctl32.lib;gdiplus.lib;shlwapi.lib;wininet.lib;version.lib;msimg32.lib;%(AdditionalDependencies) - $(WiX)SDK\VS2010\lib\x86;%(AdditionalLibraryDirectories) + $(WiX)SDK\VS2013\lib\x86;%(AdditionalLibraryDirectories) wixstdba.def true Windows @@ -89,7 +89,7 @@ Disabled - $(WiX)SDK\VS2010\inc;%(AdditionalIncludeDirectories) + $(WiX)SDK\VS2013\inc;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);$(ExtraDefinitions) true EnableFastChecks @@ -107,7 +107,7 @@ dutil.lib;balutil.lib;comctl32.lib;gdiplus.lib;shlwapi.lib;wininet.lib;version.lib;msimg32.lib;%(AdditionalDependencies) - $(WiX)SDK\VS2010\lib\x86;%(AdditionalLibraryDirectories) + $(WiX)SDK\VS2013\lib\x86;%(AdditionalLibraryDirectories) wixstdba.def false Windows